前端上传文件实时显示进度条和上传速度的工作原理是怎样的?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端上传文件实时显示进度条和上传速度的工作原理是怎样的?相关的知识,希望对你有一定的参考价值。

参考技术A

后端的责任。

前端上传文件实时显示进度条和上传速度的工作原理就是后端的责任,在Django中实现需要重载上传文件的函数,在上传时文件是被分成数个MB的chunk处理的,每次都会调用这个上传函数。也就是说,每处理一个chunk就更新uploadedsize,然后浏览器端通过AJAX获取这个值和文件大小
最后用javascript渲染到页面上。

前端只能说会用框架和插件干活。前段时间用的百度的webuploader,demo就带进度条的。js代码不多可以看一下,猜测是监听事件。上传是前端和通信协议做的事,后端是写入。在比较传统流和和spring自带的transferto的耗时统称上传时间是不对的,应为写入时间。

项目框架采用spring+hibernate+springMVC如果上传文件不想使用flash那么你可以采用html5;截图前段模块是bootstarp框架;不废话直接来代码;spring-mvc配置文件。

nginx话lua可以拿到链接的套接口,读取套接口就可以知道当前上传了多少了。可以看下openresty的lualib/resty/upload.lua。

.net网站的文件上传读取进度条和断点下载

文件上传到服务器时的进度读取

 UpfileResult result = new UpfileResult();
 try
 {
     //先把文件预读到内存里,同时计算上传进度

     IServiceProvider provider = (IServiceProvider)HttpContext.Current;
     // 返回 HTTP 请求正文已被读取的部分。    
     HttpWorkerRequest request = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
     // 获取上传大小 
     long length = long.Parse(request.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength));
     CheckFileSize(length, info);

     int bytesRead = 0; // 已读数据大小
     int read;          // 当前读取的块的大小 
     int count = 8192;  // 分块大小    
     byte[] buffer;     // 保存所有上传的数据   
     byte[] tempBuff = null;
     if (request != null)
     {
         tempBuff = request.GetPreloadedEntityBody();
     }
     if (tempBuff != null)
     {
         buffer = new byte[length];

         // 分块大小   
         count = tempBuff.Length;

         // 将已上传数据复制过去      
         Buffer.BlockCopy(tempBuff, 0, buffer, bytesRead, count);

         // 开始记录已上传大小       
         bytesRead = tempBuff.Length;

         //计算当前上传文件的百分比
         long percent = bytesRead * 100 / length;
         SetPercent(SessionId, percent);

         int ii = 0;
         // 循环分块读取,直到所有数据读取结束         
         while (request.IsClientConnected() && !request.IsEntireEntityBodyIsPreloaded() && bytesRead < length)
         {
             // 如果最后一块大小小于分块大小,则重新分块       
             if (bytesRead + count > length)
             {
                 count = (int)(length - bytesRead);
                 tempBuff = new byte[count];
             }
             // 分块读取      
             read = request.ReadEntityBody(tempBuff, count);
             // 复制已读数据块     
             Buffer.BlockCopy(tempBuff, 0, buffer, bytesRead, read);

             // 记录已上传大小   
             bytesRead += read;

             ii++;
             if (ii % 10 == 0)
             {
                 //计算当前上传文件的百分比
                 percent = bytesRead * 100 / length;
                 SetPercent(SessionId, percent);
             }
             if (read == 0)
             {
                 //如果前面读取过文件(Global.asax中的360验证),这里就读不到数据,需避免进入死循环
                 break;
             }
         }
         if (request.IsClientConnected() && !request.IsEntireEntityBodyIsPreloaded())
         {
             // 传入已上传完的数据      //     
             BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
             Type type = request.GetType();
             while ((type != null) && (type.FullName != "System.Web.Hosting.ISAPIWorkerRequest"))
             {
                 type = type.BaseType;
             }
             if (type != null)
             {
                 type.GetField("_contentAvailLength", bindingFlags).SetValue(request, buffer.Length);
                 type.GetField("_contentTotalLength", bindingFlags).SetValue(request, buffer.Length);
                 type.GetField("_preloadedContent", bindingFlags).SetValue(request, buffer);
                 type.GetField("_preloadedContentRead", bindingFlags).SetValue(request, true);
             }
             // 表示上传已结束   
         }
     }

     HttpPostedFile imgFile = _request.Files[0];
     string fileName = imgFile.FileName;
     UpLoadFile(result, imgFile, fileName, info, SessionId);
     SetPercent(SessionId, 100);
     return result;
 }
 catch (Exception ex)
 {
     result.IsOk = false;
     result.ErrMsg = ex.Message;
     return result;
 }

服务器文件的断点下载

/// <summary>
/// 支持断点下载
/// </summary>
/// <param name="Request"></param>
/// <param name="Response"></param>
/// <param name="filePath">文件的物理地址</param>
private void Down(HttpRequest Request, HttpResponse Response, string filePath)
{
    /*断点下载与普通模式不一样的是:
     断点下载的请求头信息里面增加一个属性 RANGE: bytes=100000-
     响应头里 增加一个属性 Content-Range=bytes 100000-19999/20000
     返回状态码为206(Partial Content)
     
    表示头500个字节:Range: bytes=0-499
    表示第二个500字节:Range: bytes=500-999
    表示最后500个字节:Range: bytes=-500
    表示500字节以后的范围:Range: bytes=500-
    第一个和最后一个字节:Range: bytes=0-0,-1
    同时指定几个范围:Range: bytes=500-600,601-999
    */

    // 打开文件
    using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        // 获取文件的大小
        long fileSize = fileStream.Length;
        string range = Request.Headers["Range"];

        Response.Clear();
        Response.AddHeader("Accept-Ranges", "bytes");
        Response.AddHeader("Connection", "Keep-Alive");
        Response.Buffer = false;

        //数据包大小
        int pack = 10240; //10Kb
        int sleep = 0;
        if(this.Speed.HasValue)
        {
            sleep = (int)Math.Floor(1000.0 * 10 / this.Speed.Value);
        }

        long begin = 0;
        long end = fileSize - 1;

        if (!string.IsNullOrEmpty(range))
        {
            //获取请求的范围,如果有多个范围,暂时只处理第一个,Range: bytes=500-600,601-999
            range = range.Replace("bytes=", "");
            range = range.Split(‘,‘)[0];
            string[] region = range.Split(‘-‘);
            string startPoint = region[0].Trim();
            string endPoint = region[1].Trim();
            if (startPoint == "")
            {
                long.TryParse(endPoint, out end);
                begin = fileSize - end;
                end = fileSize - 1;
            }
            else if (endPoint == "")
            {
                long.TryParse(startPoint, out begin);
                end = fileSize - 1;
            }
            else
            {
                long.TryParse(startPoint, out begin);
                long.TryParse(endPoint, out end);
            }

            Response.StatusCode = 206;
            Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", begin, end, fileSize));
        }
        //以字节流返回数据
        Response.ContentType = "application/octet-stream";
        //Response.ContentEncoding = System.Text.Encoding.UTF8;
        Response.AddHeader("Content-Disposition", "attachment; filename=\"" + HttpUtility.UrlEncode(Request.ContentEncoding.GetBytes(this.FileName)) + "\"");

        long size = end - begin + 1;
        Response.AddHeader("Content-Length", size.ToString());

        // 创建一比特数组
        byte[] buffer = new Byte[pack];
        fileStream.Position = begin;    //设置当前流位置
        // 当文件大小大于0是进入循环
        while (size > 0)
        {
            // 判断客户端是否仍连接在服务器
            if (Response.IsClientConnected)
            {
                // 获取缓冲区中的总字节数.
                int length = fileStream.Read(buffer, 0, pack);

                // 写入数据
                Response.OutputStream.Write(buffer, 0, length);

                // 将缓冲区的输出发送到客户端
                Response.Flush();

                buffer = new Byte[pack];
                size = size - length;

                if (sleep > 0)
                {
                    Thread.Sleep(sleep);
                }
            }
            else
            {
                //当用户断开后退出循环
                size = -1;
            }
        }
    }
}

文件的断点上传,待补充

 

以上是关于前端上传文件实时显示进度条和上传速度的工作原理是怎样的?的主要内容,如果未能解决你的问题,请参考以下文章

从进度条和alert的出现顺序来了解浏览器 UI 渲染 & JS进程

哪个javascript框架支持ajax方式的文件上传

基于Jquery插件Uploadify实现实时显示进度条上传图片

SpringBoot超大文件上传如何实现?

使用jquery.form.js实现文件上传及进度条前端代码

也说文件上传之兼容IE789的进度条---丢掉flash