代码之家  ›  专栏  ›  技术社区  ›  André Miranda

将上传的文件保存到MemoryStream[重复]

  •  1
  • André Miranda  · 技术社区  · 16 年前

    将一个流的内容复制到另一个流中的最佳方法是什么?有标准的实用方法吗?

    0 回复  |  直到 11 年前
        1
  •  720
  •   Nick    7 年前

    从。NET 4.5上,有 Stream.CopyToAsync method

    input.CopyToAsync(output);
    

    这将返回一个 Task 完成后可以继续,如下所示:

    await input.CopyToAsync(output)
    
    // Code from here on will be run in a continuation.
    

    请注意,这取决于呼叫的位置 CopyToAsync 如果已创建,则后续代码可能会也可能不会在调用它的同一线程上继续。

    SynchronizationContext 通话时捕捉到的 await 将决定在哪个线程上执行延续。

    此外,此调用(这是一个可能更改的实现细节)仍然会对读取和写入进行排序(它只是不会在I/O完成时浪费线程阻塞)。

    从。NET 4.0上,有 Stream.CopyTo method

    input.CopyTo(output);
    

    为了。NET 3.5及之前版本

    框架中没有任何东西可以帮助实现这一点;您必须手动复制内容,如下所示:

    public static void CopyStream(Stream input, Stream output)
    {
        byte[] buffer = new byte[32768];
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            output.Write (buffer, 0, read);
        }
    }
    

    注1:此方法将允许您报告进度(到目前为止读取了x个字节…)
    注2:为什么使用固定的缓冲区大小而不是 input.Length ?因为该长度可能不可用!从 docs :

    如果从Stream派生的类不支持查找,则对Length、SetLength、Position和Seek的调用会抛出NotSupportedException。

        2
  •  69
  •   Liam Joshua    5 年前

    MemoryStream .WriteTo(outstream);

    以及。NET 4.0已经 .CopyTo

    .NET 4.0:

    instream.CopyTo(outstream);
    
        3
  •  32
  •   Eloff    17 年前

    我使用以下扩展方法。当一个流是MemoryStream时,他们优化了重载。

        public static void CopyTo(this Stream src, Stream dest)
        {
            int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
            byte[] buffer = new byte[size];
            int n;
            do
            {
                n = src.Read(buffer, 0, buffer.Length);
                dest.Write(buffer, 0, n);
            } while (n != 0);           
        }
    
        public static void CopyTo(this MemoryStream src, Stream dest)
        {
            dest.Write(src.GetBuffer(), (int)src.Position, (int)(src.Length - src.Position));
        }
    
        public static void CopyTo(this Stream src, MemoryStream dest)
        {
            if (src.CanSeek)
            {
                int pos = (int)dest.Position;
                int length = (int)(src.Length - src.Position) + pos;
                dest.SetLength(length); 
    
                while(pos < length)                
                    pos += src.Read(dest.GetBuffer(), pos, length - pos);
            }
            else
                src.CopyTo((Stream)dest);
        }
    
        4
  •  1
  •   fryguybob    17 年前

    .NET Framework 4引入了系统流类的新“CopyTo”方法。IO命名空间。使用这种方法,我们可以将一个流复制到不同流类的另一个流。

    这里有一个例子。

        FileStream objFileStream = File.Open(Server.MapPath("TextFile.txt"), FileMode.Open);
        Response.Write(string.Format("FileStream Content length: {0}", objFileStream.Length.ToString()));
    
        MemoryStream objMemoryStream = new MemoryStream();
    
        // Copy File Stream to Memory Stream using CopyTo method
        objFileStream.CopyTo(objMemoryStream);
        Response.Write("<br/><br/>");
        Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString()));
        Response.Write("<br/><br/>");
    
        5
  •  1
  •   Bill the Lizard    14 年前

    实际上,有一种不那么严厉的流拷贝方式。但是请注意,这意味着您可以将整个文件存储在内存中。如果您处理的文件大小达到或超过数百兆字节,请不要随意尝试使用此功能。

    public static void CopySmallTextStream(Stream input, Stream output)
    {
      using (StreamReader reader = new StreamReader(input))
      using (StreamWriter writer = new StreamWriter(output))
      {
        writer.Write(reader.ReadToEnd());
      }
    }
    

    注意:二进制数据和字符编码也可能存在一些问题。

        6
  •  1
  •   hannasm    6 年前

    区分“CopyStream”实现的基本问题是:

    • 读取缓冲区的大小
    • 写入的大小
    • 我们可以使用多个线程(边读边写)吗。

    这些问题的答案导致CopyStream的实现方式大不相同,并且取决于您拥有什么样的流以及您试图优化什么。“最佳”实现甚至需要知道流正在读取和写入哪些特定硬件。

        7
  •  0
  •   Tamas Czinege    17 年前

    不幸的是,没有真正简单的解决方案。你可以试试这样的东西:

    Stream s1, s2;
    byte[] buffer = new byte[4096];
    int bytesRead = 0;
    while (bytesRead = s1.Read(buffer, 0, buffer.Length) > 0) s2.Write(buffer, 0, bytesRead);
    s1.Close(); s2.Close();
    

    但是,如果没有可读取的内容,Stream类的不同实现可能会有不同的行为。从本地硬盘驱动器读取文件的流可能会阻塞,直到读取操作从磁盘读取了足够的数据来填充缓冲区,并且只有在到达文件末尾时才会返回较少的数据。另一方面,即使还有更多数据要接收,从网络读取的流也可能返回更少的数据。

    在使用通用解决方案之前,请务必检查您正在使用的特定流类的文档。

        8
  •  0
  •   Coderer    17 年前

    根据您使用的流类型,可能有一种更有效的方法来实现这一点。如果可以将一个或两个流转换为MemoryStream,则可以使用GetBuffer方法直接处理表示数据的字节数组。这允许您使用Array等方法。CopyTo,它抽象了fryguybob提出的所有问题。你可以信任。NET知道复制数据的最佳方式。

        9
  •  0
  •   Kronass    17 年前

    public static void CopyStream(Stream input, Stream output)
    {
        byte[] buffer = new byte[32768];
        long TempPos = input.Position;
        while (true)    
        {
            int read = input.Read (buffer, 0, buffer.Length);
            if (read <= 0)
                return;
            output.Write (buffer, 0, read);
        }
        input.Position = TempPos;// or you make Position = 0 to set it at the start
    }
    

    但如果它在运行时不使用过程,则应使用内存流

    Stream output = new MemoryStream();
    byte[] buffer = new byte[32768]; // or you specify the size you want of your buffer
    long TempPos = input.Position;
    while (true)    
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
     }
        input.Position = TempPos;// or you make Position = 0 to set it at the start
    
        10
  •  0
  •   mdonatas    14 年前

    由于这些答案都没有涵盖从一个流复制到另一个流的异步方式,因此我在端口转发应用程序中成功地使用了一种模式,将数据从一个网络流复制到了另一个。它缺乏异常处理来强调模式。

    const int BUFFER_SIZE = 4096;
    
    static byte[] bufferForRead = new byte[BUFFER_SIZE];
    static byte[] bufferForWrite = new byte[BUFFER_SIZE];
    
    static Stream sourceStream = new MemoryStream();
    static Stream destinationStream = new MemoryStream();
    
    static void Main(string[] args)
    {
        // Initial read from source stream
        sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
    }
    
    private static void BeginReadCallback(IAsyncResult asyncRes)
    {
        // Finish reading from source stream
        int bytesRead = sourceStream.EndRead(asyncRes);
        // Make a copy of the buffer as we'll start another read immediately
        Array.Copy(bufferForRead, 0, bufferForWrite, 0, bytesRead);
        // Write copied buffer to destination stream
        destinationStream.BeginWrite(bufferForWrite, 0, bytesRead, BeginWriteCallback, null);
        // Start the next read (looks like async recursion I guess)
        sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
    }
    
    private static void BeginWriteCallback(IAsyncResult asyncRes)
    {
        // Finish writing to destination stream
        destinationStream.EndWrite(asyncRes);
    }
    
        11
  •  0
  •   ntiago    13 年前

    为了。NET 3.5及试用前:

    MemoryStream1.WriteTo(MemoryStream2);
    
        12
  •  0
  •   Graham Laight    8 年前

    简单安全-从原始来源创建新流:

        MemoryStream source = new MemoryStream(byteArray);
        MemoryStream copy = new MemoryStream(byteArray);
    
        13
  •  -3
  •   HasH    6 年前

    以下代码用于解决此问题,使用CopyTo将流复制到MemoryStream

    Stream stream = new MemoryStream();
    

    //任何函数都需要输入流。在mycase中将PDF文件保存为流 文件。保存(流式传输);

    MemoryStream newMs = (MemoryStream)stream;
    
    byte[] getByte = newMs.ToArray();
    

    //注意-请在finally块中处理流,而不是在内部使用块,因为它会抛出错误“流关闭时拒绝访问”

    推荐文章