将流写入文件无法正常工作

5
我正在制作一个以块为单位通过TCP传输文件的程序。每次传输文件时,似乎末尾有一小部分未被写入,因此如果我尝试传输图片在右下角有故障。我的第一个想法是,在读取、传输和写入最后一个小于缓冲区长度的块时,我也写入了很多零。所以我试图相应地改变最后一个缓冲区大小。然后我使用一个只有HELLO WORLD的小文本文件进行了测试,但当我将其写入并打开文件时,它是空的。
以下是读取和发送代码,其中range[0]是第一部分,range[1]是最后一部分:
byte[] buffer = new byte[DATA_BUFF_SIZE];
using (Stream input = File.OpenRead(file.Path))
{
    Console.WriteLine("SENT PARTS # ");
    for (int i = range[0]; i <= range[1]; i++)
    {
        Console.Write("PART " + i + ", ");

        if (i == range[1])
        {
            buffer = new byte[input.Length - input.Position];
        }

        input.Position = i * DATA_BUFF_SIZE;
        input.Read(buffer, 0, buffer.Length);

        netStream.Write(buffer, 0, buffer.Length);

    }
    Console.WriteLine("LENGTH = " + input.Length);
}

这是接收和写入代码:

int bytesReceived = 0;
int index = partRange.First;
int partNum = 0;
byte[] receiveBuffer = new byte[BUFF_SIZE];

if (index == partRange.Last)
{
    receiveBuffer = new byte[fileToDownload.Length - index * BUFF_SIZE];
}

while ((bytesReceived = netStream.Read(receiveBuffer, 0, receiveBuffer.Length)) > 0)
{
    Console.Write("INDEX:" + index + ", ");
    output.Position = index * BUFF_SIZE;
    output.Write(receiveBuffer, 0, receiveBuffer.Length);
    index++;
    partNum++;

    if (partNum > (partRange.Last - partRange.First))
    {
        break;
    }

    if (index == partRange.Last)
    {
        receiveBuffer = new byte[fileToDownload.Length - index * BUFF_SIZE];
    }
}
Console.WriteLine();

我错过了什么?我甚至在客户端和服务器上打印出缓冲区,它们是相等的。
任何帮助都将不胜感激,谢谢。

@René Vogt 抱歉,我尝试使用stackoverflow的代码按钮,但它搞乱了所有缩进,我找不到如何突出显示它。下次我会更好地格式化它。 - Foxman
3个回答

2

尝试使用netStream.Flush();

我怀疑发生的情况是在写入完成之前流已关闭。有时,输出流会继续异步写入。处理文件流时也会遇到这种情况。使用Flush()可以强制流在继续执行之前完成其正在进行的任何写操作。


0
output.Write(receiveBuffer, 0, receiveBuffer.Length);

需要

output.Write(receiveBuffer, 0, bytesReceived);

并非每次调用netStream.Read都会填充receiveBuffer。

可能还有其他错误,但这是我注意到的最大的一个。

编辑:看起来发送方也有同样的错误,但你甚至没有保存input.Read(的结果


1
同时更改输入和输出流的位置也没有什么意义。 - Camilo Terevinto
@Scott Chamberlain 我调整了缓冲区。如果长度参数小于缓冲区长度,那么当达到长度时它会截断缓冲区吗? - Foxman
@cFrozenDeath 这似乎是错误的,但它是一个多线程程序的一部分,所以位置不会每次都前进,而是会发生很大的变化。 - Foxman
2
@Foxman 如果你尝试将一个长度为900的数组写入1000的缓冲区,就会出现OutOfRangeException。顺便说一句,你的帖子中完全没有提到多线程。 - Camilo Terevinto

0

更新:


没有大小限制,缓冲区是唯一的限制。在4.5.1中似乎为81920字节。 Stream.CopyTo 的物理实现在这里提供:

private void InternalCopyTo(Stream destination, int bufferSize)
{
     Contract.Requires(destination != null);
     Contract.Requires(CanRead);
     Contract.Requires(destination.CanWrite);
     Contract.Requires(bufferSize > 0);

     byte[] buffer = new byte[bufferSize];
     int read;
     while ((read = Read(buffer, 0, buffer.Length)) != 0)
          destination.Write(buffer, 0, read);
}

你可以简单地做到以下几点,让生活变得更轻松:

input.CopyTo(output);

这是一个快速复制数据的快捷方式。复制从当前流的当前位置开始,并且在复制操作完成后不会重置目标流的位置。

using(MemoryStream destination = new MemoryStream())
     using(FileStream source = File.Open("..."))
     {
          source.CopyTo(destination);
     }

你可以很容易地使用这种方法创建一个函数:

public static MemoryStream GetStream(this FileStream source)
{
     using(MemoryStream stream = new MemoryStream())
     {
          source.CopyTo(stream);
          return stream;
     }
}

你可能需要错误处理,但是正如你所看到的,这种方法在流之间复制数据时非常灵活。应该能够将此方法弯曲到您特定的开发需求上。您可以在Microsoft Developer Network上找到有关该方法的更多信息here


这只适用于小流吗?我正在处理分块传输的数据,所以不能一次性传输整个流。 - Foxman
@Foxman 在4.5.1版本中,它有一个固定的缓冲区大小为81920字节,但是也有一个超载函数可以自定义缓冲区大小。数据的大小并不重要,实现信息在这里:http://referencesource.microsoft.com/#mscorlib/system/io/stream.cs,98ac7cf3acb04bb1 - Greg

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