zoukankan      html  css  js  c++  java
  • HTTP 下载文件的一些记录

      最近用 HttpWebRequest 做了个文件下载, HTTP 可以提供一些头信息的返回, 不过本身不带MD5之类的文件效验信息, 在做下载前可以跟本地文件进行一个对比, 如果跟本地文件相同的话就不需要下载了.

      HTTP的头返回中有两个有用信息 : ContentLength(文件大小) 和 "Last-Modified"(上次操作文件的时间).

      ContentLength : 如果跟本地文件的长度不一样, 那就是不同了, 可以直接下载.

      Last-Modified : 文件上次操作的时间, 这个不是很有用, 比如说有两个版本的文件 [ABC] 的1.0和2.0版本, 它们的长度相同, 然后放到服务器上的时间也相同, 它们的 Last-Modified 是一样的, 这样如果有版本控制需要按顺序下载的话, 1.0的先下载下来了, 本地文件的FileInfo中不管是 LastAccessTime 还是 CreationTime 都是本地写入文件的时间, 然后在更新2.0版本的时候时间对比就失效了...

      所以上面两个属性的对比都是不靠谱的, 只能作为辅助, 我们最需要的是文件的MD5, 之前想的是写一个接口能够计算并返回文件MD5, 然后用PHP之类的配置很简单就能用的话, 不管哪种系统都能方便用起来了不是, 可是我们想要最简洁的方案, 能不配环境就不配, 能不写代码就不写, 只需添加一个版本MD5文件就行了, 包含该版本所有文件的MD5信息, 跟Unity的AssetBundleManifest一样, 当版本不同的时候就先下载这个MD5列表, 然后跟本地文件对比, 不同的就下载即可.

      -- 代码 --

         public static async Task<FileUpdateInfo> CheckFileNeedUpdate(string url, string localPath, int minGapSec = 10, string md5 = null,
                CatchExceptionEnum catchExceptionEnum = CatchExceptionEnum.None)
            {
                var info = new FileUpdateInfo() { needUpdate = false, url = url, fileLength = 0, acceptRanges = false, remoteMD5 = md5 };
    
                // check MD5
                if(string.IsNullOrEmpty(info.remoteMD5) == false)
                {
                    info.localMD5 = GetMD5HashFromFile(localPath);
                    if(string.IsNullOrEmpty(info.localMD5) == false && string.Equals(info.localMD5, info.remoteMD5, StringComparison.OrdinalIgnoreCase))
                    {
                        return info;
                    }
                }
    
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
                try
                {
                    request.ServicePoint.Expect100Continue = false;//解决第二次请求失败问题
                    request.Method = "HEAD";
                    var response = await request.GetResponseAsync() as HttpWebResponse;
                    if(response.StatusCode == HttpStatusCode.OK)
                    {
                        info.fileLength = response.ContentLength;               // get length
                        var acceptRanges = response.Headers.Get(AcceptRanges);  // get can use ranged
                        info.acceptRanges = acceptRanges.Equals("bytes", StringComparison.OrdinalIgnoreCase);
    
                        FileInfo fileInfo = new FileInfo(localPath);
                        if(fileInfo.Exists == false || fileInfo.Length != response.ContentLength)
                        {
                            info.needUpdate = true;
                            return info;
                        }
                        var date = response.Headers.Get(LastModified);
                        var serverFileWriteTime = System.DateTime.Parse(date);
                        var span = serverFileWriteTime - fileInfo.CreationTime;
                        if(span.TotalSeconds >= minGapSec)
                        {
                            info.needUpdate = true;
                            return info;
                        }
                        // last check MD5
                        if(string.IsNullOrEmpty(info.remoteMD5))
                        {
                            info.needUpdate = true;
                            return info;
                        }
                        if(string.Equals(string.IsNullOrEmpty(info.localMD5) ? (info.localMD5 = GetMD5HashFromFile(localPath)) : info.localMD5, info.remoteMD5, StringComparison.OrdinalIgnoreCase) == false)
                        {
                            info.needUpdate = true;
                            return info;
                        }
                    }
                }
                catch(System.Exception ex)
                {
                    if((catchExceptionEnum & CatchExceptionEnum.ShowCached) != 0)
                    {
                        Common.Debug.LogError(ex.Message);
                    }
                    if((catchExceptionEnum & CatchExceptionEnum.Catch) != 0)
                    {
                        throw ex;
                    }
                }
                finally
                {
                    request.Abort();
                }
    
                return info;
            }

      这里还是有几个注意点的, request.ServicePoint.Expect100Continue = false; 和 request.Abort(); 才能保证发起多次请求不被服务器卡住, 如果不加的话只能从服务器下载一个文件, 然后之后的请求都没有响应了, 很危险.

      首先是MD5对比, 如果传入了服务器上的文件的MD5, 跟本地的对比, 如果文件一致的话就不用下载了.

      然后是获取HEAD信息, 因为我使用的是分段下载的方式, 需要获得服务器是否支持分段传输, 头信息中有个 "Accept-Ranges" 信息, 如果类型是bytes就是支持分段传输了, 断点续传就是基于它的.

      之后的长度对比和文件操作时间对比, 也能算是文件对比了.

      最后的MD5对比, 证明确实需要更新文件...

      就这样如果是需要更新的文件, 会返回得到新文件的长度信息, 可以按照需求进行下载了.

      Unity 的 WWW.LoadFromCacheOrDownload() 是怎样实现远程文件比对的呢, 好奇

  • 相关阅读:
    搭建strom 的开发环境
    maven 的plugin 的使用
    Maven 的dependency 的 classifier的作用
    Maven中的dependency的scope作用域详解
    Supervisor-进程监控自动重启
    websocket 实战
    vue 监听路由变化
    vux-uploader 图片上传组件
    vue 定义全局函数
    判断对象属性的值是否空,如为空,删除该属性
  • 原文地址:https://www.cnblogs.com/tiancaiwrk/p/15016010.html
Copyright © 2011-2022 走看看