通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大 小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了,电断了没 有上传完成,需要客户重新上传,这是致命的,所以对于大文件上传的要求最基本的是断点续传。
什么是断点续传:
引用百度百科:断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个 部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传 下载未完成的部分,而没有必要从头开始上传下载,断点续传可以提高节省操作时间,提高用户体验性。
如下图:
上传流程如下: 1、上传前先把文件分成块 2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传 3、各分块上传完成最后合并文件
文件下载则同理。
为了更好的理解文件分块上传的原理,下边用java代码测试文件的分块与合并。
文件分块的流程如下: 1、获取源文件长度 2、根据设定的分块文件的大小计算出块数 3、从源文件读数据依次向每一个块文件写数据。
//测试文件分块
@Test
public void testChunk() throws IOException {
//源文件
File sourceFile = new File("E:\\ffmpeg_test\\lucene.avi");
//块文件目录
String chunkFileFolder = "E:\\ffmpeg_test\\chunks\\";
//先定义块文件大小
long chunkFileSize = 1 * 1024 * 1024;
//块数
long chunkFileNum = (long) Math.ceil(sourceFile.length() * 1.0 /chunkFileSize);
//创建读文件的对象
RandomAccessFile raf_read = new RandomAccessFile(sourceFile,"r");
//缓冲区
byte[] b = new byte[1024];
for(int i=0;i<chunkFileNum;i++){
//块文件
File chunkFile = new File(chunkFileFolder+i);
//创建向块文件的写对象
RandomAccessFile raf_write = new RandomAccessFile(chunkFile,"rw");
int len = -1;
while((len = raf_read.read(b))!=-1){
raf_write.write(b,0,len);
//如果块文件的大小达到 1M开始写下一块儿
if(chunkFile.length()>=chunkFileSize){
break;
}
}
raf_write.close();
}
raf_read.close();
}
文件合并流程: 1、找到要合并的文件并按文件合并的先后进行排序。 2、创建合并文件 3、依次从合并的文件中读取数据向合并文件写入数
//测试文件合并
@Test
public void testMergeFile() throws IOException {
//块文件目录
String chunkFileFolderPath = "E:\\ffmpeg_test\\chunks\\";
//块文件目录对象
File chunkFileFolder = new File(chunkFileFolderPath);
//块文件列表
File[] files = chunkFileFolder.listFiles();
//将块文件排序,按名称升序
List<File> fileList = Arrays.asList(files);
Collections.sort(fileList, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
if(Integer.parseInt(o1.getName())>Integer.parseInt(o2.getName())){
return 1;
}
return -1;
}
});
//合并文件
File mergeFile = new File("E:\\ffmpeg_test\\lucene_merge.avi");
//创建新文件
boolean newFile = mergeFile.createNewFile();
//创建写对象
RandomAccessFile raf_write = new RandomAccessFile(mergeFile,"rw");
byte[] b = new byte[1024];
for(File chunkFile:fileList){
//创建一个读块文件的对象
RandomAccessFile raf_read = new RandomAccessFile(chunkFile,"r");
int len = -1;
while((len = raf_read.read(b))!=-1){
raf_write.write(b,0,len);
}
raf_read.close();
}
raf_write.close();
}
如何在web页面实现断点续传?
常见的方案有: 1、通过Flash上传,比如SWFupload、Uploadify。 2、安装浏览器插件,变相的pc客户端,用的比较少。 3、Html5
随着html5的流行,本项目采用Html5完成文件分块上传。 本项目使用WebUploader完成大文件上传功能的开发,WebUploader官网地址:
http://fexteam.gz01.bdysite.com/webuploader/
特性如下:
使用WebUploader上传流程如下:
在webuploader中提供很多钩子方法,下边列出一些重要的:
本项目使用如下钩子方法: 1)before-send-file 在开始对文件分块儿之前调用,可以做一些上传文件前的准备工作,比如检查文件目录是否创建完成等。
2)before-send 在上传文件分块之前调用此方法,可以请求服务端检查分块是否存在,如果已存在则此分块儿不再上传。
3)after-send-file 在所有分块上传完成后触发,可以请求服务端合并分块文件。
注册钩子方法源代码:
WebUploader.Uploader.register({
"before‐send‐file": "beforeSendFile",
"before‐send": "beforeSend",
"after‐send‐file": "afterSendFile"
}
使用webUploader前需要创建webUploader对象。 指定上传分块的地址:/api/media/upload/uploadchunk
this.uploader = WebUploader.create({
swf: "/static/plugins/webuploader/dist/Uploader.swf", //上传文件的flash文件,浏览器不支持h5时启动
flashserver:"/api/media/upload/uploadchunk",//上传分块的服务端地址,注意跨域问题
fileVal:"file",//文件上传域的name
pick:"#picker",//指定选择文件的按钮容器
auto:false,//手动触发上传
disableGlobalDnd:true,//禁掉整个页面的拖拽功能
chunked:true,// 是否分块上传
chunkSize:1*1024*1024, // 分块大小(默认5M)
threads:3, // 开启多个线程(默认3个)
prepareNextFile:true// 允许在文件传输时提前把下一个文件准备好
}
)
文件开始上传前前端请求服务端准备上传工作。 参考源代码如下:
type: "POST",
url: "/api/media/upload/register",
data: {
// 文件唯一表示
fileMd5:this.fileMd5,
fileName: file.name,
fileSize:file.size,
mimetype:file.type,
fileExt:file.ext
}
上传分块前前端请求服务端校验分块是否存在。 参考源代码如下:
type:"POST",
url:"/api/media/upload/checkchunk",
data:{
// 文件唯一表示
fileMd5:this.fileMd5,
// 当前分块下标
chunk:block.chunk,
// 当前分块大小
chunkSize:block.end‐block.start
}
在所有分块上传完成后触发,可以请求服务端合并分块文件 参考代码如下:
type:"POST",
url:"/api/media/upload/mergechunks",
data:{
fileMd5:this.fileMd5,
fileName: file.name,
fileSize:file.size,
mimetype:file.type,
fileExt:file.ext
}
服务端需要实现如下功能: 1、上传前检查上传环境 检查文件是否上传,已上传则直接返回。 检查文件上传路径是否存在,不存在则创建。 2、分块检查 检查分块文件是否上传,已上传则返回true。 未上传则检查上传路径是否存在,不存在则创建。 3、分块上传 将分块文件上传到指定的路径。 4、合并分块 将所有分块文件合并为一个文件。 在数据库记录文件信息。