将MemoryStream保存和加载到/从文件中

377

我正在将一个结构体序列化为一个MemoryStream,我想保存和加载已序列化的结构体。

那么,如何将MemoryStream保存到文件中并从文件中加载它?


1
如果你需要保存到文件,为什么要使用MemoryStream - Oded
@Oded 我应该使用什么?你能给我一个例子吗? - Mahdi Ghiasi
9个回答

444

您可以使用 MemoryStream.WriteToStream.CopyTo 方法(支持框架版本 4.5.2、4.5.1、4.5 和 4)将内存流的内容写入另一个流。

memoryStream.WriteTo(fileStream);

更新:

fileStream.CopyTo(memoryStream);
memoryStream.CopyTo(fileStream);

19
memoryStream.CopyTo 对我似乎无效,而 WriteTo 可以。我想可能是因为我的 memoryStream.Position 不为 0。 - Mark Adamson
20
没错,这是正确的。它们之间的区别在于CopyTo从当前位置开始复制,而不像WriteTo一样总是从开头开始。 - AnorZaken
13
在使用 CopyTo 方法之前,添加 [file|memory]Stream.Seek(0, SeekOrigin.Begin); 会将当前位置设置为 0,以便 CopyTo 将复制整个流。 - Martin Backasch
2
谢谢。你让它变得如此简单,只用了两行代码。 - Darpan Gupta

329

假设MemoryStream的变量名为ms

以下代码将MemoryStream写入文件:

using (FileStream file = new FileStream("file.bin", FileMode.Create, System.IO.FileAccess.Write)) {
   byte[] bytes = new byte[ms.Length];
   ms.Read(bytes, 0, (int)ms.Length);
   file.Write(bytes, 0, bytes.Length);
   ms.Close();
}

这将一个文件读取到内存流中:

using (MemoryStream ms = new MemoryStream())
using (FileStream file = new FileStream("file.bin", FileMode.Open, FileAccess.Read)) {
   byte[] bytes = new byte[file.Length];
   file.Read(bytes, 0, (int)file.Length);
   ms.Write(bytes, 0, (int)file.Length);
}
在 .Net Framework 4+ 中,你可以简单地将 FileStream 复制到 MemoryStream,并按如下方式简单地进行反转:
MemoryStream ms = new MemoryStream();
using (FileStream file = new FileStream("file.bin", FileMode.Open, FileAccess.Read))
    file.CopyTo(ms);

反之亦然(从MemoryStream到FileStream):

using (FileStream file = new FileStream("file.bin", FileMode.Create, System.IO.FileAccess.Write))
    ms.CopyTo(file);

1
我可以问一下,在读取示例中为什么要使用FileMode.Create而不是FileMode.Open吗? - Philter
7
在第一个代码块中,你可以使用内置的 ms.ToArray() 函数,而不是手动将内存流复制到数组中。 - Gman
10
重要的是将 ms.Position = 0,否则字节数组(和文件)将包含所有零。 - Gregory Khrapunovich
1
@Fernando68,“using (...){ }”结构具有完全相同的效果。 - Fabricio Araujo
3
提醒其他人,使用(FileStream)和(ms.CopyTo(file))会将位置设置为文件的末尾,之后您需要重置内存流。 - Rebecca
@GregoryKhrapunovich,这就像是一盒卡带,我们在制作它的时候会一直写到最后。现在,在我们把它放进随身听之前,必须将卡带倒回去。 - Paras Parmar

78

即使发生异常(在文件I/O方面很可能会发生),也应该正确处理流 - 使用using语句是我最喜欢的解决方法,因此,在编写MemoryStream时,您可以使用以下代码:

using (FileStream file = new FileStream("file.bin", FileMode.Create, FileAccess.Write)) {
    memoryStream.WriteTo(file);
}

而且要读回来:

using (FileStream file = new FileStream("file.bin", FileMode.Open, FileAccess.Read)) {
    byte[] bytes = new byte[file.Length];
    file.Read(bytes, 0, (int)file.Length);
    ms.Write(bytes, 0, (int)file.Length);
}

如果文件很大,那么值得注意的是,读取操作将使用的内存量是文件总大小的两倍。解决这个问题的一个方法是从字节数组创建MemoryStream——以下代码假定您不会写入该流。

MemoryStream ms = new MemoryStream(bytes, writable: false);

我的研究(下文)表明,内部缓冲区与您传递的字节数组相同,因此应该节省内存。

byte[] testData = new byte[] { 104, 105, 121, 97 };
var ms = new MemoryStream(testData, 0, 4, false, true);
Assert.AreSame(testData, ms.GetBuffer());

69
var memoryStream = new MemoryStream(File.ReadAllBytes("1.dat"));

File.WriteAllBytes("1.dat", memoryStream.ToArray()); 

.GetBuffer 可以在某些情况下用于获取内部缓冲区,而不是分配第二个数组:

byte[] bytes = File.ReadAllBytes("1.dat");
var memoryStream = new MemoryStream(bytes, 0, bytes.Length, true, true); 

File.WriteAllBytes("1.dat", memoryStream.GetBuffer()); 

2
请记住,这将重新分配整个缓冲区(MemoryStream.ToArray)byte[] copy = GC.AllocateUninitializedArray<byte>(count); - CorrM

24

写入文件的结合答案可以是:

MemoryStream ms = new MemoryStream();    
FileStream file = new FileStream("file.bin", FileMode.Create, FileAccess.Write);
ms.WriteTo(file);
file.Close();
ms.Close();

19

保存到文件中

Car car = new Car();
car.Name = "Some fancy car";
MemoryStream stream = Serializer.SerializeToStream(car);
System.IO.File.WriteAllBytes(fileName, stream.ToArray());

从文件中加载

using (var stream = new MemoryStream(System.IO.File.ReadAllBytes(fileName)))
{
    Car car = (Car)Serializer.DeserializeFromStream(stream);
}

何处

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Serialization
{
    public class Serializer
    {
        public static MemoryStream SerializeToStream(object o)
        {
            MemoryStream stream = new MemoryStream();
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, o);
            return stream;
        }

        public static object DeserializeFromStream(MemoryStream stream)
        {
            IFormatter formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            object o = formatter.Deserialize(stream);
            return o;
        }
    }
}

这个类的实现最初发布于这里

以及

[Serializable]
public class Car
{
    public string Name;
}

19

对于加载文件,我更喜欢这个方法

MemoryStream ms = new MemoryStream();
using (FileStream fs = File.OpenRead(file))
{
    fs.CopyTo(ms);
}

如果文件在Microsoft Word中打开 - 是否有一种方法可以从该文件创建内存流?我收到了一个错误“文件正在被另一个进程打开”。 - FrenkyB
@FrenkyB 我也经常遇到这个问题。如果你在Word或其他应用程序中打开了文件,则无法执行此操作。只需关闭Word中的文件即可。 - Kristopher
@FrenkyB,你能做一个File.Copy吗?我发现这个方法可行,然后从那个文件中读取流并删除新文件...虽然很糟糕,但似乎可以工作。 - ridecar2

4

我使用面板控件添加图像或甚至流视频,但你可以将图像保存在 SQL Server 中作为Image或在 MySQL 中作为largeblob。这段代码对我来说非常有效。试试看。

在这里,你可以保存图像。

MemoryStream ms = new MemoryStream();
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, panel1.Bounds);
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // here you can change the Image format
byte[] Pic_arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(Pic_arr, 0, Pic_arr.Length);
ms.Close();

这里可以加载,但我使用了PictureBox控件。

MemoryStream ms = new MemoryStream(picarr);
ms.Seek(0, SeekOrigin.Begin);
fotos.pictureBox1.Image = System.Drawing.Image.FromStream(ms);

希望能有所帮助。


3
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;

namespace ImageWriterUtil
{
    public class ImageWaterMarkBuilder
    {
        //private ImageWaterMarkBuilder()
        //{
        //}
        Stream imageStream;
        string watermarkText = "©8Bytes.Technology";
        Font font = new System.Drawing.Font("Brush Script MT", 30, FontStyle.Bold, GraphicsUnit.Pixel);
        Brush brush = new SolidBrush(Color.Black);
        Point position;
        public ImageWaterMarkBuilder AddStream(Stream imageStream)
        {
            this.imageStream = imageStream;
            return this;
        }
        public ImageWaterMarkBuilder AddWaterMark(string watermarkText)
        {
            this.watermarkText = watermarkText;
            return this;
        }
        public ImageWaterMarkBuilder AddFont(Font font)
        {
            this.font = font;
            return this;
        }

        public ImageWaterMarkBuilder AddFontColour(Color color)
        {
            this.brush = new SolidBrush(color);
            return this;
        }
        public ImageWaterMarkBuilder AddPosition(Point position)
        {
            this.position = position;
            return this;
        }

        public void CompileAndSave(string filePath)
        {

            //Read the File into a Bitmap.
            using (Bitmap bmp = new Bitmap(this.imageStream, false))
            {
                using (Graphics grp = Graphics.FromImage(bmp))
                {


                    //Determine the size of the Watermark text.
                    SizeF textSize = new SizeF();
                    textSize = grp.MeasureString(watermarkText, font);

                    //Position the text and draw it on the image.
                    if (position == null)
                        position = new Point((bmp.Width - ((int)textSize.Width + 10)), (bmp.Height - ((int)textSize.Height + 10)));
                    grp.DrawString(watermarkText, font, brush, position);

                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        //Save the Watermarked image to the MemoryStream.
                        bmp.Save(memoryStream, ImageFormat.Png);
                        memoryStream.Position = 0;
                       // string fileName = Path.GetFileNameWithoutExtension(filePath);
                        // outPuthFilePath = Path.Combine(Path.GetDirectoryName(filePath), fileName + "_outputh.png");
                        using (FileStream file = new FileStream(filePath, FileMode.Create, System.IO.FileAccess.Write))
                        {
                            byte[] bytes = new byte[memoryStream.Length];
                            memoryStream.Read(bytes, 0, (int)memoryStream.Length);
                            file.Write(bytes, 0, bytes.Length);
                            memoryStream.Close();
                        }
                    }
                }
            }

        }
    }
}

用法:

ImageWaterMarkBuilder.AddStream(stream).AddWaterMark("").CompileAndSave(filePath);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接