c# 如何发送仍在写入的文件流并一直发送到创建结束

标签 c# rest io stream download

我有一个包含返回流的方法的 Restful Web 服务,调用此方法会导致下载与该流关联的文件。

现在,出于性能目的,我需要在创建文件时下载它。 这是该方法的实际代码:

    [WebGet(UriTemplate = "SendFileStream/")]
    public Stream SendFileStream()
    {
        //This thread generates a big file
        FileCreatorThread fileCreator = new FileCreatorThread();
        Thread fileCreatorThread = new Thread(fileCreator.CreateFile);
        fileCreatorThread.Start();
        WebOperationContext.Current.OutgoingResponse.Headers.Add(HttpResponseHeader.Expires, DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
        WebOperationContext.Current.OutgoingResponse.ContentType = "multipart/related";
        FileStream fs = new FileStream(@"c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.Write);
        return fs;
    }

此方法效果很好,文件在创建时可下载,不会抛出 IOException。 但问题是下载比创建快,一到流的末尾就停止了,没有下载还需要创建的部分。

所以我的问题是,有没有办法在文件创建结束之前保持下载挂起(假设我们事先不知道这个文件的最终长度)而不需要第二种方法来检查下载的文件大小是否与实际文件的长度相同,以及重新开始下载直到完成的方法?

预先感谢您的帮助。

PS:对于那些想知道如何使用两个不同的线程以读写方式访问文件的人,这里是生成文件的线程的代码。

    public class FileCreatorThread
    {
        public void CreateFile()
        {
            FileStream fs = new FileStream(@"c:\test.txt", FileMode.Create, FileAccess.Write, FileShare.Read);
            StreamWriter writer = new StreamWriter(fs);
            for (int i = 0; i < 1000000; i++)
            {
                writer.WriteLine("New line number " + i);
                writer.Flush();
                //Thread.Sleep(1);
            }
            writer.Close();
            fs.Close();
        }
    }

解决方案: 如果最终找到了我的问题的解决方案,主要基于这两个页面:

首先,我在网络服务中启用了流式传输。 这是来自 web.config 文件的代码片段,有关详细信息,请参阅上面的链接

<bindings>
  <webHttpBinding>
    <binding name="httpsStream" transferMode="Streamed" maxReceivedMessageSize="67108864">
      <security mode="Transport"/>
    </binding>        
  </webHttpBinding>            
</bindings>

其次,我创建了一个带有 read() 方法重载的自定义流,该方法检查流的末尾是否已被重新读取,如果是,则等待几毫秒并重试 read() 以确保这是真的文件结尾。

这里是自定义流的代码:

public class BigFileStream : Stream
    {
        FileStream inStream;
        FileStream testStream;
        String filePath;

        internal BigFileStream(string filePath)
        {
            this.filePath = filePath;
            inStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Write);
        }

        public override bool CanRead
        {
            get { return inStream.CanRead; }
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override void Flush()
        {
            throw new Exception("This stream does not support writing.");
        }

        public override long Length
        {
            get { throw new Exception("This stream does not support the Length property."); }
        }

        public override long Position
        {
            get
            {
                return inStream.Position;
            }
            set
            {
                throw new Exception("This stream does not support setting the Position property.");
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int countRead = inStream.Read(buffer, offset, count);
            if (countRead != 0)
            {
                return countRead;
            }
            else
            {
                for (int i = 1; i < 10; i++)
                {
                    Thread.Sleep(i * 15);
                    countRead = inStream.Read(buffer, offset, count);
                    if (countRead != 0)
                    {
                        return countRead;
                    }
                }
                return countRead;
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new Exception("This stream does not support seeking.");
        }

        public override void SetLength(long value)
        {
            throw new Exception("This stream does not support setting the Length.");
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("This stream does not support writing.");
        }

        public override void Close()
        {
            inStream.Close();
            base.Close();
        }

        protected override void Dispose(bool disposing)
        {
            inStream.Dispose();
            base.Dispose(disposing);
        }
    }

下载方法现在将返回:

return new BigFileStream(@"c:\test.txt");

这可能不是最简洁或最有效的方法,所以请不要犹豫,发表评论并提出其他解决方案。

更新 2:

这是 Read() 方法的一个更高效的版本,因为如果作者花费超过 815 毫秒来写入更多字节,我第一次发布的方法仍然会失败。

public override int Read(byte[] buffer, int offset, int count)
        {
            int countRead = inStream.Read(buffer, offset, count);
            if (countRead != 0)
            {
                return countRead;
            }
            else
            {
                Boolean fileAccessible = false;
                while (!fileAccessible)
                {
                    try
                    {
                        //try to open the file in Write, if it goes in exception, that means that the file is still opened by the writer
                        testStream = new FileStream(this.filePath, FileMode.Open, FileAccess.Write, FileShare.Read);
                        testStream.Close();
                        break;
                    }
                    catch (Exception e)
                    {
                        Thread.Sleep(500);
                        countRead = inStream.Read(buffer, offset, count);
                        if (countRead != 0)
                        {
                            return countRead;
                        }
                    }
                }
                countRead = inStream.Read(buffer, offset, count);
                return countRead;                  
            }
        }

最佳答案

您遇到的问题是因为您试图获取整个文件而不是文件的一部分。

当一个文件从一侧写入时,另一侧应该同时请求一个段。假设每个周期 4096 字节,然后在段达到文件长度时创建整个文件。

我知道有一种方法可以使用 WCF 进行异步网络服务调用。

试着朝这个方向解决你的问题。

如果您还有问题,请联系我。

关于c# 如何发送仍在写入的文件流并一直发送到创建结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19535764/

相关文章:

linux - 为什么 MongoDB 在 Debian 上比在 Windows 上慢..?

c - 如何在 C 中打开带有用户输入变量的文件?

c# - 处理动态生成的表中的按钮点击

javascript - 使用 jQuery Ajax 将单个对象传递到 MVC Controller 方法

c# - ListView.SelectedItems[0] 选择错误

c# - 如何正确地将 bindingSource 更改提交到源数据库?

c# - 如何从 WCF REST 方法返回自定义类型值的字典作为常规 JSON 对象?

java - 使用 Spring 在 Java 中发送文本 REST 消息

java - 我可以使用多线程一次多次进行相同的休息调用吗?

python - 转换代码以在windows中读取文件到linux