zoukankan      html  css  js  c++  java
  • aspnetcore 实现断点续传

    扩展方法,改编自http://blogs.visigo.com/chriscoulson/easy-handling-of-http-range-requests-in-asp-net/

    /// <summary>
            /// 断点下载
            /// </summary>
            /// <param name="controller"></param>
            /// <param name="fullpath"></param>
            /// <returns></returns>
            public static async Task<long> RangeDownload(this Controller controller, string fullpath)
            {
                long size, start, end, length, fp = 0;
                using (StreamReader reader = new StreamReader(File.OpenRead(fullpath)))
                {
    
                    size = reader.BaseStream.Length;
                    start = 0;
                    end = size - 1;
                    length = size;
                    // Now that we've gotten so far without errors we send the accept range header
                    /* At the moment we only support single ranges.
                     * Multiple ranges requires some more work to ensure it works correctly
                     * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
                     *
                     * Multirange support annouces itself with:
                     * header('Accept-Ranges: bytes');
                     *
                     * Multirange content must be sent with multipart/byteranges mediatype,
                     * (mediatype = mimetype)
                     * as well as a boundry header to indicate the various chunks of data.
                     */
                    controller.Response.Headers.Add("Accept-Ranges", "0-" + size);
                    // header('Accept-Ranges: bytes');
                    // multipart/byteranges
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
    
                    if (!String.IsNullOrEmpty(controller.Request.Headers["HTTP_RANGE"]))
                    {
                        long anotherStart = start;
                        long anotherEnd = end;
                        string[] arr_split = controller.Request.Headers["HTTP_RANGE"].FirstOrDefault().Split(new char[] { Convert.ToChar("=") });
                        string range = arr_split[1];
    
                        // Make sure the client hasn't sent us a multibyte range
                        if (range.IndexOf(",") > -1)
                        {
                            // (?) Shoud this be issued here, or should the first
                            // range be used? Or should the header be ignored and
                            // we output the whole content?
                            controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
                            controller.Response.StatusCode = 416;
                            controller.Response.StatusCode = 416;
                            await controller.Response.WriteAsync("Requested Range Not Satisfiable");
                        }
    
                        // If the range starts with an '-' we start from the beginning
                        // If not, we forward the file pointer
                        // And make sure to get the end byte if spesified
                        if (range.StartsWith("-"))
                        {
                            // The n-number of the last bytes is requested
                            anotherStart = size - Convert.ToInt64(range.Substring(1));
                        }
                        else
                        {
                            arr_split = range.Split(new char[] { Convert.ToChar("-") });
                            anotherStart = Convert.ToInt64(arr_split[0]);
                            long temp = 0;
                            anotherEnd = (arr_split.Length > 1 && Int64.TryParse(arr_split[1].ToString(), out temp)) ? Convert.ToInt64(arr_split[1]) : size;
                        }
                        /* Check the range and make sure it's treated according to the specs.
                         * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
                         */
                        // End bytes can not be larger than $end.
                        anotherEnd = (anotherEnd > end) ? end : anotherEnd;
                        // Validate the requested range and return an error if it's not correct.
                        if (anotherStart > anotherEnd || anotherStart > size - 1 || anotherEnd >= size)
                        {
    
                            controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
                            controller.Response.StatusCode = 416;
                            await controller.Response.WriteAsync( "Requested Range Not Satisfiable");
                        }
                        start = anotherStart;
                        end = anotherEnd;
    
                        length = end - start + 1; // Calculate new content length
                        fp = reader.BaseStream.Seek(start, SeekOrigin.Begin);
                        controller.Response.StatusCode = 206;
                    }
                }
                // Notify the client the byte range we'll be outputting
                controller.Response.Headers.Add("Content-Range", "bytes " + start + "-" + end + "/" + size);
                controller.Response.Headers.Add("Content-Length", length.ToString());
                // Start buffered download
                await controller.Response.SendFileAsync(fullpath, fp, length);
                return fp;
            }

    控制器方法内调用样例:

    /// <summary>
            /// 下载指定版本
            /// </summary>
            /// <param name="id"></param>
            /// <param name="version"></param>
            /// <returns></returns>
            public async Task Download(Guid id, int version)
            {
                string path = null;
                var state = appService.GetAppState(id, version);
                if (state == Model.AppState.Available)
                {
                    path = appService.GetZipFromAppList(id, version);
                    if (!System.IO.File.Exists(path))
                    {
                        await this.StatusCode(404).ExecuteResultAsync(this.ControllerContext);
                    }
                    var offset = await this.RangeDownload(path);
                    if (offset == 0)
                    {
                        appService.IncreaseNDownload(state, id, version);
                    }
                }
                else
                {
                    await this.StatusCode(404).ExecuteResultAsync(this.ControllerContext);
                }
    }
  • 相关阅读:
    BZOJ 1834 Luogu P2604 [ZJOI2010]网络扩容 (最小费用最大流)
    BZOJ 1565 Luogu P2805 [NOI2009]植物大战僵尸 (Tarjan判环、最小割)
    BZOJ 3993 Luogu P3324 [SDOI2015]星际战争 (最大流、二分答案)
    BZOJ 3277 串 & BZOJ 3473 字符串 (广义后缀自动机、时间复杂度分析)
    BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)
    Mongodb的聚合和管道
    Mongodb文档查询
    Mongodb的基本操作-数据库 集合 文档的增删改查
    Linux下Mongodb的安装
    mongdb的优势和不足
  • 原文地址:https://www.cnblogs.com/mrtiny/p/aspnetcore_range_download.html
Copyright © 2011-2022 走看看