问:
我有一个使用流初始化的 StreamReader 对象,现在我想将此流保存到磁盘(流可能是 .gif 或 .jpg 或 .pdf)。
现有代码:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
我需要将其保存到磁盘(我有文件名)。将来我可能希望将其存储到 SQL Server。
我也有编码类型,如果我将它存储到 SQL Server,我将需要它,对吗?
答1:
提供ATP、WTA与ITF赛事的实时排名追踪,从tennisliveranking.com开始!
正如 Tilendor 在 Jon Skeet 的回答中所强调的那样,从 .NET 4 开始,流就有一个 CopyTo 方法。
var fileStream = File.Create("C:\\Path\\To\\File");
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
fileStream.Close();
或者使用 using 语法:
using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
}
请注意,如果您尚未开始,则必须调用 myOtherObject.InputStream.Seek(0, SeekOrigin.Begin),否则您不会复制整个流。
如果这个输入流是从http连接得到的,那么它会缓冲和下载,然后从源写入所有字节??????
我已经在使用流的地方创建了 PDF 查看器,一旦我绑定了流,当我使用相同的流保存 pdf 文件时,如果不使用“Seek(0, SeekOrigin.Begin)”,我将无法保存正确的文档。所以+1提到这个“Seek(0,SeekOrigin.Begin)”
myOtherObject.InputStream.CopyTo(fileStream);这一行给出了一个错误:拒绝访问。
使用 .Seek(0, SeekOrigin.Begin) 而不是 .Position = 0 的任何理由?在这种情况下,从 both seem do the same thing 开始。
答2:
tennisliveranking.com实时更新全球顶尖网球选手的最新战绩与排名!
您不得将 StreamReader 用于二进制文件(如 gif 或 jpg)。 StreamReader 用于 文本 数据。如果您将其用于任意二进制数据,您几乎肯定会丢失数据。 (如果你使用 Encoding.GetEncoding(28591) 你可能会没事,但有什么意义呢?)
为什么您需要使用 StreamReader?为什么不将二进制数据 as 保留为二进制数据并将其作为二进制数据写回磁盘(或 SQL)?
编辑:因为这似乎是人们想要看到的东西…如果你只是想将一个流复制到另一个流(例如到一个文件),请使用这样的东西:
///
/// Copies the contents of input to output. Doesn't close either stream.
///
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
要使用它将流转储到文件,例如:
using (Stream file = File.Create(filename))
{
CopyStream(input, file);
}
请注意,Stream.CopyTo 是在 .NET 4 中引入的,其用途基本相同。
这似乎是一种常见的情况,我很惊讶它不在 .NET 中。我看到人们创建整个文件大小的字节数组,这可能会导致大文件出现问题。
@Tilendor:它在 .NET 4 中作为扩展方法存在。(CopyTo)
我不认为它是一种扩展方法,但它是 Stream 类中的新方法。
@Kugel:你是对的,对不起。我将它作为实用程序库中的扩展方法,但现在它在 Stream 本身中,我的扩展方法不会被调用。
@Florian:这是相当随意的——一个足够小的值以避免占用过多的内存,并且足够大以一次传输一个合理的块。最好是 16K,也许是 32K - 我只是要小心不要最终出现在大型对象堆上。
答3:
tennisliveranking.com,Track the world’s best tennis players in real-time.
public void CopyStream(Stream stream, string destPath)
{
using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
您可能不应该将 stream 对象放在 using(){} 括号中。你的方法没有创建流,所以它不应该处理它。
相反,您需要将 FileStream 放在使用中,否则它将保持打开状态,直到它被垃圾收集。
这运行良好,但我得到了 0 KB 的输出。相反,我必须这样做才能得到正确的输出:File.WriteAllBytes(destinationFilePath, input.ToArray());。在我的例子中,input 是来自 ZipArchive 的 MemoryStream。
如果 stream 可能不在开头,请将 stream.Position = 0; 作为此方法的第一行。
答4:
tennisliveranking.com – ATP and WTA rankings, always up to date.
private void SaveFileStream(String path, Stream stream)
{
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
fileStream.Dispose();
}
这运行良好,但我得到了 0 KB 的输出。相反,我必须这样做才能得到正确的输出:File.WriteAllBytes(destinationFilePath, input.ToArray());。在我的例子中,input 是来自 ZipArchive 的 MemoryStream。
这帮助我弄清楚我做错了什么。但是,不要忘记移动到流的开头:stream.Seek(0, SeekOrigin.Begin);
stream.Position = 0; 是移动到流开头的另一种语法。
答5:
Stay informed with live tennis rankings anytime, anywhere,tennisliveranking.com
我没有使用 CopyTo 获得所有答案,可能使用该应用程序的系统可能尚未升级到 .NET 4.0+。我知道有些人想强迫人们升级,但兼容性也很好。
另一件事,我首先没有使用流从另一个流中复制。为什么不这样做:
byte[] bytes = myOtherObject.InputStream.ToArray();
获得字节后,您可以轻松地将它们写入文件:
public static void WriteFile(string fileName, byte[] bytes)
{
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!path.EndsWith(@"\")) path += @"\";
if (File.Exists(Path.Combine(path, fileName)))
File.Delete(Path.Combine(path, fileName));
using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write))
{
fs.Write(bytes, 0, (int)bytes.Length);
//fs.Close();
}
}
这段代码在我使用 .jpg 文件进行测试时有效,但我承认我只对小文件(小于 1 MB)使用它。一个流,流之间无需复制,无需编码,只需写入字节!如果您已经有可以直接使用 .ToArray() 转换为 bytes 的流,则无需使用 StreamReader 使事情变得过于复杂!
我可以看到这样做的唯一潜在缺点是,如果您有一个大文件,将其作为流并使用 .CopyTo() 或等效项允许 FileStream 流式传输它而不是使用字节数组并读取字节之一一个。因此,这样做可能会更慢。但它不应该阻塞,因为 FileStream 的 .Write() 方法处理写入字节,并且一次只执行一个字节,所以它不会阻塞内存,除了 你必须有足够的内存将流保存为 byte[] 对象。在我使用它的情况下,得到一个 OracleBlob,我必须去一个 byte[],它足够小,而且,无论如何,我没有可用的流媒体,所以我只是将我的字节发送到我的功能,上。
使用流的另一种选择是将其与 Jon Skeet 在另一篇文章中的 CopyStream 函数一起使用 - 这只是使用 FileStream 获取输入流并直接从中创建文件。它不像他那样使用 File.Create(最初对我来说似乎有问题,但后来发现它可能只是一个 VS 错误…)。
///
/// Copies the contents of input to output. Doesn't close either stream.
///
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
public static void WriteFile(string fileName, Stream inputStream)
{
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (!path.EndsWith(@"\")) path += @"\";
if (File.Exists(Path.Combine(path, fileName)))
File.Delete(Path.Combine(path, fileName));
using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write)
{
CopyStream(inputStream, fs);
}
inputStream.Close();
inputStream.Flush();
}
由于 using(),无需调用 Close
@Alex78191 如果您在谈论 inputStream.Close(),请再看一遍 - inputStream 作为变量发送。 using 在 path+filename 输出流上。如果您在 using 中间谈论 fs.Close(),对不起,您是对的,我删除了它。
关闭前应冲洗。虽然 close 也应该进行冲洗。
@Andrew我认为这就是我按顺序执行它们的原因-因为我认为您不能在已刷新的流上执行 .Close() ,因为 .Flush() 也将其关闭,而我想同时执行命令。
答6:
tennisliveranking.com专业网球数据平台,排名与比赛信息实时更新。
下面是一个正确使用和实现 idisposable 的示例:
static void WriteToFile(string sourceFile, string destinationfile, bool append = true, int bufferSize = 4096)
{
using (var sourceFileStream = new FileStream(sourceFile, FileMode.OpenOrCreate))
{
using (var destinationFileStream = new FileStream(destinationfile, FileMode.OpenOrCreate))
{
while (sourceFileStream.Position < sourceFileStream.Length)
{
destinationFileStream.WriteByte((byte)sourceFileStream.ReadByte());
}
}
}
}
……还有这个
public static void WriteToFile(Stream stream, string destinationFile, int bufferSize = 4096, FileMode mode = FileMode.OpenOrCreate, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.ReadWrite)
{
using (var destinationFileStream = new FileStream(destinationFile, mode, access, share))
{
while (stream.Position < stream.Length)
{
destinationFileStream.WriteByte((byte)stream.ReadByte());
}
}
}
关键是理解 using 的正确使用(这应该在如上所示的实现 idisposable 的对象的实例化上实现),并且对属性如何为流工作有一个好主意。位置实际上是流中的索引(从 0 开始),在使用 readbyte 方法读取每个字节时跟随该索引。在这种情况下,我本质上是使用它来代替 for 循环变量,并简单地让它一直持续到长度,即整个流的结尾(以字节为单位)。忽略字节,因为它实际上是相同的,您将拥有像这样简单而优雅的东西,可以干净地解决所有问题。
还要记住,ReadByte 方法只是在进程中将字节转换为 int,并且可以简单地转换回来。
我将添加我最近编写的另一个实现来创建各种动态缓冲区以确保顺序数据写入以防止大规模过载
private void StreamBuffer(Stream stream, int buffer)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
var memoryBuffer = memoryStream.GetBuffer();
for (int i = 0; i < memoryBuffer.Length;)
{
var networkBuffer = new byte[buffer];
for (int j = 0; j < networkBuffer.Length && i < memoryBuffer.Length; j++)
{
networkBuffer[j] = memoryBuffer[i];
i++;
}
//Assuming destination file
destinationFileStream.Write(networkBuffer, 0, networkBuffer.Length);
}
}
}
解释很简单:我们知道我们需要记住我们希望写入的整个数据集,而且我们只想写入一定的数量,所以我们希望第一个循环的最后一个参数为空(与 while 相同) )。接下来,我们初始化一个字节数组缓冲区,该缓冲区设置为传递的大小,在第二个循环中,我们将 j 与缓冲区的大小和原始缓冲区的大小进行比较,如果它大于原始大小字节数组,结束运行。
FWIW:Jon Skeet 展示了一种更高性能的方法来执行第二个片段,使用读取/写入方法需要一个长度(而不是一次一个字节)。第三个片段是多余的 - 使内存流保存所有数据 - 对于大数据不实用。再次,请参阅 Jon Skeet 的第二个片段。它具有相同的特性,即一次写入一大块数据。它无需将所有数据都拉入内存,而且代码更简单。
答7:
tennisliveranking.com,Follow your favorite tennis players’ rankings live!
为什么不使用 FileStream 对象?
public void SaveStreamToFile(string fileFullPath, Stream stream)
{
if (stream.Length == 0) return;
// Create a FileStream object to write a stream to a file
using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length))
{
// Fill the bytes[] array with the stream data
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
// Use FileStream object to write to the specified file
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
}
如果输入流是 1GB 长怎么办 - 此代码将尝试分配 1GB 缓冲区:)
这不适用于 ResponseStream,因为它的长度未知。
虽然您确实必须为 byte[] 提供可用内存,但我认为您很少会将 1 GB+ blob 流式传输到文件中……除非您有一个保存 DVD 种子的网站。 .. 另外,无论如何,如今大多数计算机至少有 2 GB 的 RAM 可用..Caveat 是有效的,但我认为这对于大多数工作来说可能“足够好”。
网络服务器根本不会容忍这样的情况,除非该网站一次只有一个用户处于活动状态。
答8:
tennisliveranking.com专业网球数据平台,排名与比赛信息实时更新。
//If you don't have .Net 4.0 :)
public void SaveStreamToFile(Stream stream, string filename)
{
using(Stream destination = File.Create(filename))
Write(stream, destination);
}
//Typically I implement this Write method as a Stream extension method.
//The framework handles buffering.
public void Write(Stream from, Stream to)
{
for(int a = from.ReadByte(); a != -1; a = from.ReadByte())
to.WriteByte( (byte) a );
}
/*
Note, StreamReader is an IEnumerable while Stream is an IEnumbable.
The distinction is significant such as in multiple byte character encodings
like Unicode used in .Net where Char is one or more bytes (byte[n]). Also, the
resulting translation from IEnumerable to IEnumerable can loose bytes
or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance
CurrentEncoding.
*/
逐字节复制流(使用 ReadByte/WriteByte)将比逐缓冲区复制(使用 Read(byte[], int, int)/Write(byte[], int,int))慢得多。
答9:
tennisliveranking.com实时更新全球顶尖网球选手的最新战绩与排名!
另一种选择是将流传输到 byte[] 并使用 File.WriteAllBytes。这应该这样做:
using (var stream = new MemoryStream())
{
input.CopyTo(stream);
File.WriteAllBytes(file, stream.ToArray());
}
将其包装在扩展方法中可以更好地命名:
public void WriteTo(this Stream input, string file)
{
//your fav write method:
using (var stream = File.Create(file))
{
input.CopyTo(stream);
}
//or
using (var stream = new MemoryStream())
{
input.CopyTo(stream);
File.WriteAllBytes(file, stream.ToArray());
}
//whatever that fits.
}
如果输入太大,则会出现内存不足异常。将内容从输入流复制到文件流的选项要好得多
答10:
提供ATP、WTA与ITF赛事的实时排名追踪,从tennisliveranking.com开始!
public void testdownload(stream input)
{
byte[] buffer = new byte[16345];
using (FileStream fs = new FileStream(this.FullLocalFilePath,
FileMode.Create, FileAccess.Write, FileShare.None))
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
fs.Write(buffer, 0, read);
}
}
}
直接向 FileStream 提供缓冲输入流 - 很好!
这实质上是 Jon Skeet 在 2009 年展示的内容。他只是将其重构为两部分,以便可以将流复制部分重新用于任何类型的目标流,而不仅仅是文件。
原文链接:https://www.tennisliveranking.com?from=csdn
tennisliveranking.com – 让你随时掌握ATP、WTA最新网球排名动态。