大文件上传

0、项目源码地址

源码地址 :https://github.com/zhuchangwu/large-file-upload

它是个demo,仅供参考

前端基于 vue-simple-uploader (感谢这个大佬)实现: https://github.com/simple-uploader/vue-uploader/blob/master/README_zh-CN.md

vue-simple-uploader底层封装了uploader.js : https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md

1、如何唯一标识一个文件?

文件的信息后端会存储在mysql数据库表中。

在上传之前,前端通过 spark-md5.js 计算文件的md5值以此去唯一的标示一个文件。

spark-md5.js 地址:https://github.com/satazor/js-spark-md5

README.md中有spark-md5.js的使用demo,可以去看看。

2、断点续传是如何实现的?

断点续传可以实现这样的功能,比如用户上传200M的文件,当用户上传完199M时,断网了,有了断点续传的功能,我们允许RD再次上传时,能从第199M的位置重新上传。

实现原理:

实现断点续传的前提是,大文件切片上传。然后前端得问后端哪些chunk曾经上传过,让前端跳过这些上传过的chunk就好了。

前端的上传器(uploader.js)在上传时会先发送一个GET请求,这个请求不会携带任何chunk数据,作用就是向后端询问哪些chunk曾经上传过。 后端会将这些数据保存在mysql数据库表中。比如按这种格式:1:2:3:5表示,曾经上传过的分片有1,2,3,5。第四片没有被上传,前端会跳过1,2,3,5。 仅仅会将第四个chunk发送给后端。

3、秒传是如何实现的?

秒传实现的功能是:当RD重复上传一份相同的文件时,除了第一次上传会正常发送上传请求后,其他的上传都会跳过真正的上传,直接显示秒成功。

实现方式:

后端存储着当前文件的相关信息。为了实现秒传,我们需要搞一个字段(isUploaded)表示当前md5对应的文件是否曾经上传过。 后端在处理 前端的上传器(uploader.js)发送的第一个GET请求时,会将这个字段发送给前端,比如 isUploaded = true。前端看到这个信息后,直接跳过上传,显示上传成功。

4、上传暂停是如何实现的?

上传的暂停:并不是去暂停一个已经发送出去的正在进行数据传输的http请求~

而是暂停发送起发送下一个http请求。

就我们的项目而言,因为我们的文件本来就是先切片,对于我们来说,暂停文件的上传,本质上就是暂停发送下一个chunk。

5、前端上传并发数是多少?

前端的uploader.js中默认会三条线程启动并发上传,前端会在同一时刻并发 发送3个chunk,后端就会相应的为每个请求开启三个协程处理上传的过来的chunk。

在我们的项目中,会将前端并发数调整成了1。原因如下:

因为考虑到了断点续传的实现,后端需要记录下曾经上传过哪些切片。(这个记录在mysql的数据库表中,以 ”1:2:3:4:5“ )这种格式记录。

Mysql5.7默认的存储引擎是innoDB,默认的隔离级别是RR。如果我们将前端的并发数调大,就会出现下面的异常情况:

1. goroutine1 获取开启事物,读取当前上传到记录是 1:2 (未提交事物)
2. goroutine1 在现有的记录上加上自己处理的分片3,并和现有的1:2拼接在一起成1:2:3 (未提交事物)
3. goroutine2 获取开启事物,(因为RR,所以它读不到1:2:3)读取当前上传到记录是 1:2 (未提交事物)
4. goroutine1 提交事物,将1:2:3写回到mysql
5. goroutine2 在现有的记录上加上自己处理的分片4,并和现有的1:2拼接在一起成1:2:4 (提交事物)

可以看到,如果前端并发上传,后端就会出现分片丢失的问题。 故前端将并发数置为1。

6、单个chunk上传失败怎么办?

前端会重传chunk?

由于网络问题,或者时后端处理chunk时出现的其他未知的错误,会导致chunk上传失败。

uploaded.js 中有如下的配置项, 每次uploader.js 在上传每一个切片实际上都是在发送一次post请求,后端根据这个post请求是会给前端一个状态吗。 uploader.js 就是根据这个状态码去判断是失败了还是成功了,如果失败了就会重新发送这个上传的请求。

那uploader.js是如何知道有哪些状态吗是它应该重传chunk的标记呢? 看看下面uploader.js需要的options 就明白了,其中的permantErrors中配置的状态码标示:当遇到这个状态码时整个上传直接失败~

successStatuses中配置的状态码表示chunk是上传成功的~。 其他的状态吗uploader.js 就会任务chunk上传的有问题,于是重新上传~

        options: {
target: 'http://localhost:8081/file/upload',
maxChunkRetries: 3,
permanentErrors:[502], // 永久性的上传失败~,会认为整个文件都上传失败了
successStatuses:[200], // 当前chunk上传成功后的状态吗
...
}

7、超过重传次数后,怎么办?

比如我们设置出错后重传的次数为3,那么无论当前分片是第几片,整个文件的上传状态被标记为false,这就意味着会终止所有的上传。

肯定不会出现这种情况:chunk1重传3次后失败了,chunk2还能再去上传,这样的话数据肯定不一致了。

8、如何控制上传多大的文件?

目前了解到nginx端的限制上单次上传不能超过1M。

前端会对大文件进行切片突破nginx的限制。

        options: {
target: 'http://localhost:8081/file/upload',
chunkSize: 512000, // 单次上传 512KB
}

如果后续和nginx负责的同学达成一致,可以把这个值进行调整。前端可以后续将这个chunk的阈值加大。

9、如何保证上传文件的百分百正确?

在上传文件前,前端会计算出当前RD选择的这个文件的 md5 值。

当后端检测到所有的分片全部上传完毕,这时会merge所有分片汇聚成单个文件。计算这个文件的md5 同 RD在前端提供的文件的md5值比对。 比对结果一致说明RD正确的完成了上传。结果不一致,说明文件上传失败了~返回给前端任务失败,提示RD重新上传。

10、其他细节问题:

如何判断文件上传失败了,给RD展示红色?

如何控制上传什么类型的文件?

如何控制不能上传空文件?

上面说过了,当 uploader.js 遇到了permanentErrors这种状态码时会认为文件上传失败了。

前端想在上传失败后,将进度条转换成红色,其实改一下CSS样式就好了,问题就在于,根据什么去修改?在哪里去修改?

前端会将每一个file封装成一个组件:如下图中的files就是file的集合

整个的fileList会将会被渲染成下面这样。


我们上传的文件被vue-simple-uploader的作者封装成一个file.vue组件,这个对象中会有个配置参数, 比如它会长下面这样。

     options: {
target: 'http://localhost:8081/file/upload',
statusText: {
success: '上传成功',
error: '上传出错,请重试',
typeError: '暂不支持上传您添加的文件格式',
uploading: '上传中',
emptyError:'不能上传空文件',
paused: '请确认文件后点击上传',
waiting: '等待中'
}
}
},

我们将上面的配置添加给Uploader.js

      const uploader = new Uploader(this.options)

在file组件中有如下计算属性的,分别是status和statusText

    computed: {
// 计算出一个状态信息
status () {
const isUploading = this.isUploading // 是否正在上传
const isComplete = this.isComplete // 是否已经上传完成
const isError = this.error // 是否出错了
const isTypeError = this.typeError // 是否出错了
const paused = this.paused // 是否暂停了
const isEmpty = this.emptyError // 是否暂停了
// 哪个属性先不为空,就返回哪个属性
if (isComplete) {
return 'success'
} else if (isError) {
return 'error'
} else if (isUploading) {
return 'uploading'
} else if (isTypeError) {
return 'typeError'
} else if (isEmpty) {
return 'emptyError'
} else if (paused) {
return 'paused'
} else {
return 'waiting'
}
},
// 状态文本提示信息
statusText () {
// 获取到计算出的status属性(相当于是个key,具体的值在下面的fileStatusText中获取到)
const status = this.status
// 从file的uploader对象中获取到 fileStatusText,也就是用自己定义的名字
const fileStatusText = this.file.uploader.fileStatusText
let txt = status
if (typeof fileStatusText === 'function') {
txt = fileStatusText(status, this.response)
} else {
txt = fileStatusText[status]
}
return txt || status
},
},

status绑定在html上

	<div class="uploader-file" :status="status">

对应的CSS样式入下:

  .uploader-file[status="error"] .uploader-file-progress {
background: #ffe0e0;
}

综上:有了上面代码的编写,我们可以直接像下面这样控制就好了

  file.typeError = true // 表示文件的类型不符合我们的预期,不允许RD上传
file.error = true // 表示文件上传失败了
file.emptyError = true // 表示文件为空,不允许上传

11、后端数据库表设计

CREATE TABLE `file_upload_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(64) NOT NULL COMMENT '上传文件的用户账号',
`file_name` varchar(64) NOT NULL COMMENT '上传文件名',
`md5` varchar(255) NOT NULL COMMENT '上传文件的MD5值',
`is_uploaded` int(11) DEFAULT '0' COMMENT '是否完整上传过 \n0:否\n1:是',
`has_been_uploaded` varchar(1024) DEFAULT NULL COMMENT '曾经上传过的分片号',
`url` varchar(255) DEFAULT NULL COMMENT 'bos中的url,或者是本机的url地址',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '本条记录创建时间',
`update_time` timestamp NULL DEFAULT NULL COMMENT '本条记录更新时间',
`total_chunks` int(11) DEFAULT NULL COMMENT '文件的总分片数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

12、关于什么时候mergechunk

在本文中给出的demo中,merge是后端处理完成所有的chunk后,像前端返回 merge=1,这个表示来实现的。

前端拿着这个字段去发送/merge请求去合并所有的chunk。

值得注意的地方是:这个请求是在uploader.js认为所有的分片全部成功上传后,在单个文件成功上传的回调中执行的。我想了一下,感觉这么搞其实不太友好,万一merge的过程中失败了,或者是某个chunk丢失了,chunk中的数据缺失,最终merge的产物的md5值其实并不等于原文件。当这种情况发生的时候,其实上传是失败的。但是后端既然告诉uploader.js 可以合并了,说明后端的upload函数认为任务是成功的。vue-simple-uploader上传完最后一个chunk得到的状态码是200,它也会觉得任务是成功的,于是在前端段展示绿色的上传成功给用户看~(然而上传是失败的), 这么看来,整个过程其实控制的不太好~

我现在的实现:直接干掉merge请求,前端1条线程发送请求,将chunk依次发送到后端。后端检测到所有的chunk都上传过来后主动merge,merge完成后马上校验文件的md5值是否符合预期。这个处理过程在上传最后一个chunk的请求中进行,因此可以实现的控制前端上传成功还是失败的样式~

大文件上传、断点续传、秒传、beego、vue的更多相关文章

  1. 支持IE低版本的上传 大文件切割上传 断点续传 秒传

    1. http://files.cnblogs.com/files/blackice/UploadDemo.rar 此demo是使用的 swfupload 2.http://download.csdn ...

  2. 基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件

    目录 1. 前言 2. 关于vue-simple-uploader 3. 基于vue-simple-uploader封装全局上传组件 4. 文件上传流程概览 5. 文件分片 6. MD5的计算过程 7 ...

  3. php实现大文件上传分片上传断点续传

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  4. Asp.net mvc 大文件上传 断点续传

    Asp.net mvc 大文件上传 断点续传 进度条   概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这 ...

  5. 30分钟玩转Net MVC 基于WebUploader的大文件分片上传、断网续传、秒传(文末附带demo下载)

    现在的项目开发基本上都用到了上传文件功能,或图片,或文档,或视频.我们常用的常规上传已经能够满足当前要求了, 然而有时会出现如下问题: 文件过大(比如1G以上),超出服务端的请求大小限制: 请求时间过 ...

  6. Asp.net mvc 大文件上传 断点续传 进度条

    概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这篇文章,此方法确实很不错,能够稳定的上传大文件,http: ...

  7. js实现大文件上传分片上传断点续传

    文件夹上传:从前端到后端 文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠.网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹. ...

  8. JS大文件上传断点续传解决方案

    1 背景 用户本地有一份txt或者csv文件,无论是从业务数据库导出.还是其他途径获取,当需要使用蚂蚁的大数据分析工具进行数据加工.挖掘和共创应用的时候,首先要将本地文件上传至ODPS,普通的小文件通 ...

  9. Web大文件上传断点续传解决方案

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

随机推荐

  1. 集合概述及Collection接口的常用方法

    java集合像是一种容器,可以动态的把多个对象的引用放到容器中 java的集合类可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组 package com.aff.coll; imp ...

  2. 路由器硬改+刷OpenWrt+挂载摄像头+U盘

    标题: 路由器硬改+刷OpenWrt+挂载摄像头+U盘 作者: 梦幻之心星 347369787@QQ.com 标签: [路由器, OpenWrt, 摄像头, 固件] 目录: 路由器 日期: 2019- ...

  3. nginx 配置重定向及nginx配置if

    需求:地址 http://testa/inlinePreview/live.html?id=463738305721405440重定向到 http://testb/shares/live.html?n ...

  4. 06 . Nginx静态资源缓存

    Nginx静态资源 Nginx可以处理静态资源 非Web服务器可以运行处理而生成的文件,即服务器只需要从硬盘或者缓存中读取然后直接给客户端响应即可. 常见的静态资源 # 浏览器渲染: html文件,样 ...

  5. ISTQB认证测试工程师基础大纲(2019.12.25)

    1.本文档目的: 用于生成认证测试员基础级考试题. 本大纲中除了简介和附录外,考核通常包含了所有K1级别的内容,因此,应试者可能会被考到本大纲中要求识别,牢记,或记忆的关键词或概念.在本大纲中,每章开 ...

  6. k-means聚类分析 python 代码实现(不使用现成聚类库)

    一.实验目标 1.使用 K-means 模型进行聚类,尝试使用不同的类别个数 K,并分析聚类结果. ​ 2.按照 8:2 的比例随机将数据划分为训练集和测试集,至少尝试 3 个不同的 K 值,并画出不 ...

  7. 学Linux驱动: 应该先了解驱动模型

    [导读] Linux设备林林总总,嵌入式开发一个绕不开的话题就是设备驱动开发,在做具体设备驱动开发之前,有必要对Linux设驱动模型有一个相对清晰的认识,将会帮助驱动开发,明白具体驱动接口操作符相应都 ...

  8. Java实现 蓝桥杯 传纸条

    题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个mm行nn列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运 ...

  9. Java实现 LeetCode 496 下一个更大元素 I

    496. 下一个更大元素 I 给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. nu ...

  10. Java实现 LeetCode 368 最大整除子集

    368. 最大整除子集 给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (Si,Sj) 都要满足:Si % Sj = 0 或 Sj % Si = 0. 如果有多个目标子集, ...