我有一个StreamReader
使用流初始化的对象,现在我想将此流保存到磁盘(该流可以是.gif
or .jpg
或or .pdf
)。
现有代码:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
- 我需要将其保存到磁盘(我有文件名)。
- 将来我可能要将其存储到SQL Server。
我也有编码类型,如果将其存储到SQL Server,我将需要它,对吗?
我有一个StreamReader
使用流初始化的对象,现在我想将此流保存到磁盘(该流可以是.gif
or .jpg
或or .pdf
)。
现有代码:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
我也有编码类型,如果将其存储到SQL Server,我将需要它,对吗?
Answers:
正如Tiledor在Jon Skeet的回答中所强调的那样,流CopyTo
从.NET 4开始就有一种方法。
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)
如果您还没有开始,则必须打电话,否则您将不会复制整个流。
你不能使用StreamReader
二进制文件(如gif或JPG格式)。StreamReader
用于文本数据。如果将其用于任意二进制数据,几乎可以肯定会丢失数据。(如果使用Encoding.GetEncoding(28591),可能会没事,但是有什么意义呢?)
为什么根本需要使用a StreamReader
?为什么不只将二进制数据保留为二进制数据并将其作为二进制数据写回磁盘(或SQL)呢?
编辑:由于这似乎是人们想要的东西,看看......如果你不只是要一个流复制到另一个(如一个文件)使用是这样的:
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
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中引入的,其用途基本相同。
public void CopyStream(Stream stream, string destPath)
{
using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
stream
对象放在using(){}
括号中。您的方法未创建流,因此不应丢弃它。
FileStream
改为使用,否则它将一直保持打开状态,直到被垃圾回收为止。
File.WriteAllBytes(destinationFilePath, input.ToArray());
。就我而言,input
是MemoryStream
来自的内部ZipArchive
。
private void SaveFileStream(String path, Stream stream)
{
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
fileStream.Dispose();
}
File.WriteAllBytes(destinationFilePath, input.ToArray());
。就我而言,input
是MemoryStream
来自的内部ZipArchive
。
stream.Seek(0, SeekOrigin.Begin);
我并没有获得所有答案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),但是该代码的工作方式与我对文件进行的测试相同。一个流,流之间无需复制,无需编码,只需写入字节!StreamReader
如果您已经有了视频流,则无需使用繁琐的操作即可bytes
直接使用转换为.ToArray()
!
我这样做的唯一潜在缺点是,如果您有一个大文件,将其作为流并使用.CopyTo()
或等效文件,则可以FileStream
流式传输它,而不是使用字节数组并逐字节读取字节。结果,这样做可能会更慢。但这不应该令人窒息,因为句柄的.Write()
方法会FileStream
写入字节,并且一次只能处理一个字节,因此它不会阻塞内存,除非您必须有足够的内存来将流作为byte[]
字节保存。对象。在我使用它的情况下,获取OracleBlob
,我必须转到byte[]
,它足够小,此外,无论如何我都没有可用的流,所以我只是将字节发送到上面的函数中。
使用流的另一种选择是将其与CopyStream
另一篇文章中的Jon Skeet的功能一起使用-这仅用于FileStream
获取输入流并直接从中创建文件。它没有File.Create
像他一样使用(最初对我来说似乎是个问题,但后来发现它可能只是VS错误...)。
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
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();
}
Close
因为using()
inputStream.Close()
,请再看一遍- inputStream
作为变量发送。的using
是在path+filename
输出流。如果您是fs.Close()
在中间谈论问题,那么using
抱歉,您是正确的,我已将其删除。
//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<Char> while Stream is an IEnumbable<byte>.
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<byte> to IEnumerable<Char> can loose bytes
or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance
CurrentEncoding.
*/
为什么不使用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);
}
}
byte[]
,但我认为将1 GB +的blob流传输到文件中的情况很少见……除非您有一个站点可以保存DVD种子...否则,无论如何,大多数计算机这些天至少都具有2 GB的RAM。Caveat是有效的,但我认为在这种情况下,它对于大多数工作来说可能“足够好”。
另一个选择是将流转到a 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.
}
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
-不错!
这是一个使用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(FileStream 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());
}
}
}
关键是要了解use的正确用法(应在实现如上所示的实现对象的实例上实现),并对属性如何用于流有个好主意。Position实际上是流中的索引(从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与缓冲区的大小以及原始缓冲区的大小进行比较,如果它大于原始缓冲区的大小,则对其进行比较。字节数组,结束运行。