【问题标题】:Extend a stream to allow seamless multistreams from http in C#在 C# 中扩展流以允许来自 http 的无缝多流
【发布时间】:2015-08-05 19:19:29
【问题描述】:

目前我正在使用 HttpWebRequest 和 HttpWebResponse 来检查 StatusCode 并下载文件。

我正在下载的文件是.zip 文件(我首先尝试)或.zip 文件的一部分(.z01、.z02 , 等等。)。由于零件文件的大小始终为 20 兆字节(在我的情况下),我可以检查我是否下载了 20 兆字节,如果是,请尝试继续下一部分。如果没有,我会假设我正在下载最后一部分。

为了在 Java 中实现这一点,我将扩展 InputStream 并覆盖其读取方法并返回其中的下一个流,从当前流中读取或关闭它并使用 HttpURLConnection 检索下一个流 - 或假设如果没有下一个流,则它是最后一个流。

尝试在 c# 中扩展 Stream,但由于 Stream 类是抽象的,我必须实现很多方法,我真的不需要/想要来实施。 BufferedStream 是密封的。因此,如果我不想实现 Stream 类的所有方法,我唯一的其他选择是扩展 MemoryStream

在 c# 中执行此操作的好方法是什么?

我需要一个流的原因是,我正在处理流(解压缩/解码)它——它已经完美地适用于单个 .zip 文件。因为我还可以指定偏移量,所以我可以从偏移量下载。
对于多部分流,我会将给定的偏移量转换为正确的部分文件名,方法是将其除以 20 兆字节。

编辑:我拒绝将 zip 和解码添加为标签,因为我下载的文件类型并不重要(顺便说一句:它不是真正的 zip,而是 LZMA -encoded 文件,我对其进行解码)。

【问题讨论】:

    标签: c# stream


    【解决方案1】:

    只需在不需要实际实现的方法中执行此操作...

    throw new NotImplementedException();
    

    【讨论】:

    • 我想过这一点,但虽然我可能不会使用它们,但它们可能会以某种方式在某处使用 - 这就是为什么我会避免将 CanRead / CanWrite 等设置为“抛出新的 NotImplementedException();” .
    【解决方案2】:

    我的方法可能不是最好的方法,也没有测试 Position 属性,但它确实有效。

    我从Stream 派生了一个类,并围绕它实现了一个包装器,用于设置 currentStream-object。

    服务器上的文件通常以 .zip 或 .z01、.z02、.z03 等格式保存。MultiPartStream(string) 构造函数让 MultiPartStream“猜测”正确的 URL。 GetPart() 通常在检查响应时抑制错误。 MultiPartStream(string, string) 构造函数只允许下载文件同时返回 Transferspeed。

    我可能会随时更改这门课程,但这已经满足了我的需求,而且由于我不喜欢有未解决的问题,所以我决定提供一个对我有用的回复。如果我最终更改了很多,我可能会稍后更新它。

    考虑到这一点,我可能可以将NetworkStream 子分类。也许我会改变这一点,但到目前为止,这个 MultiPartStream 运行良好,它实际上使用了我拥有的所有带宽。此外,它还具有 TotalStreamSize 和 Transferspeed。

    MultiPartStream.cs

    using MyProject.Logic;
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MyProject.Model
    {
        public class MultiPartStream : Stream
        {
            private HttpWebResponse response;
            private Stream currentStream;
            private List<KeyValuePair<string, long>> parts = new List<KeyValuePair<string,long>>();
    
            private Stopwatch timer = new Stopwatch();
            private long totalStreamSize;
            private long bytesRead;
            private string file;
            private int currentPart = 0;
    
            public MultiPartStream(string file)
            {
                this.file = file;
                GetParts();
                NextStream();
            }
    
            public MultiPartStream(string file, string url)
            {
                this.file = file;
                GetPart(url, false);
                NextStream();
            }
    
            public override int Read(byte[] buffer, int offset, int count)
            {
                if (!timer.IsRunning)
                    timer.Start();
    
                int bytesRead = currentStream.Read(buffer, offset, count);
                if (bytesRead <= 0 && currentPart < parts.Count - 1)
                {
                    // if we cannot read from the Stream anymore, we open the next Stream if one is available
                    currentPart++;
                    NextStream();
                    bytesRead = Read(buffer, offset, count);
                }
                this.bytesRead += bytesRead;
    
                return (int)bytesRead;
            }
    
            protected override void Dispose(bool disposing)
            {
                timer.Stop();
                base.Dispose(disposing);
            }
    
            private void GetParts()
            {
                if (!this.GetPart(DownloadHelper.Instance.GetSingleFileUrl(file)))
                {
                    int filePart = 1;
                    while (this.GetPart(DownloadHelper.Instance.GetMultiFileUrl(file, filePart)))
                    {
                        filePart++;
                    }
                }
            }
    
            private bool GetPart(string url, bool suppressApplicationException = true)
            {
                bool thisPartAdded = false;
    
                HttpWebRequest request = HttpWebRequest.CreateHttp(url);
    
                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    if (request.HaveResponse && response.StatusCode == HttpStatusCode.OK)
                    {
                        parts.Add(new KeyValuePair<string, long>(url, response.ContentLength));
                        totalStreamSize += response.ContentLength;
                        thisPartAdded = true;
                    }
                    response.Close();
                }
                catch (WebException e)
                {
                    if (!suppressApplicationException)
                        throw new ApplicationException(String.Format("Error: cannot access file '{0}' ({1}: {2})", url, e.Status, e.Message));
                }
    
                return thisPartAdded;
            }
    
            private void NextStream()
            {
                if (response != null)
                {
                    response.Close();
                    response = null;
                }
    
                HttpWebRequest request = HttpWebRequest.CreateHttp(parts[currentPart].Key);
    
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                    if (request.HaveResponse && response.StatusCode == HttpStatusCode.OK)
                    {
                        currentStream = response.GetResponseStream();
                    }
                }
                catch (WebException e)
                {
                    throw new ApplicationException(String.Format("Error: could not open file '{0}' for streaming ({1}: {2})", parts[currentPart].Key, e.Status, e.Message));
                }
            }
    
            public long TotalStreamSize
            {
                get { return totalStreamSize; }
            }
    
            public string TransferSpeed
            {
                get
                {
                    float transferSpeed = (float)(bytesRead / 1024f) / (timer.ElapsedMilliseconds / 1000f);
                    if (transferSpeed > 1024f)
                    {
                        return "@ " + Math.Round(transferSpeed / 1024f, 2) + " mb / s";
                    }
                    else
                    {
                        return "@ " + Math.Round(transferSpeed, 2) + " kb / s";
                    }
                }
            }
    
            #region Remaining Stream-methods
            public override long Position
            {
                get
                {
                    long valueToCompare = 0;
                    for (int index = 0; index < currentPart; index++)
                    {
                        valueToCompare += parts[index].Value;
                    }
                    return valueToCompare + currentStream.Position;
                }
                set
                {
                    long valueToCompare = 0;
                    for (int index = 0; index < parts.Count; index++)
                    {
                        valueToCompare += parts[index].Value;
                        if (value <= valueToCompare)
                        {
                            currentPart = index;
                            NextStream();
                            currentStream.Position = value - (valueToCompare - parts[index].Value);
                        }
                    }
                }
            }
    
            public override bool CanRead
            {
                get { return true; }
            }
    
            public override bool CanSeek
            {
                get { return false; }
            }
    
            public override bool CanWrite
            {
                get { return false; }
            }
    
            public override void Flush()
            {
                if (currentPart > 0)
                {
                    currentPart = 0;
                    NextStream();
                }
                else if (currentPart == 0)
                {
                    currentStream.Flush();
                }
            }
    
            public override long Length
            {
                get { return totalStreamSize; }
            }
    
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw new NotSupportedException();
            }
    
            public override void SetLength(long value)
            {
                throw new NotSupportedException();
            }
    
            public override void Write(byte[] buffer, int offset, int count)
            {
                throw new NotSupportedException();
            }
            #endregion
        }
    }
    

    其他相关方法:

    public string GetSingleFileUrl(string file)
    {
        return String.Format("{0}/Zip/{1}.zip", this.GetBaseUrl(), file.Replace(@"\", "/"));
    }
    
    public string GetMultiFileUrl(string file, int part)
    {
        return String.Format("{0}/Zip/{1}.z{2:D2}", this.GetBaseUrl(), file.Replace(@"\", "/"), part);
    }
    

    这个类并不完美,因为它是为特定的文件源严格编写的,但它确实完成了它的工作。如果我最终将它改进到我真正满意的程度,我会确保更新我的回复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-26
      • 1970-01-01
      • 2015-02-06
      • 2021-02-05
      • 2020-11-14
      • 1970-01-01
      • 1970-01-01
      • 2022-06-28
      相关资源
      最近更新 更多