plupload断点续传 php,Plupload 文件断点续传,文件分块上传
Plupload 介绍
Plupload 是一款由著名的 Web 编辑器 TinyMCE 团队开发的上传组件,简单易用且功能强大。Plupload 会自动侦测当前的浏览器环境,选择最合适的上传方式,并且会优先使用 HTML5 的方式。该前端插件实现原理简单来说就是将文件按照指定分块大小切割成 n 块,然后依次将这 n 块文件数据上传至服务端,可实现暂停、继续上传。这也算是断点续传的一种实现方式。
本人在使用过程中对于这个插件也遇到了一些坑,比如,上传暂停后重新开始,Plupload 会重复上传上次最后一块文件块;该问题的原因在于调用 Plupload 的 stop() 方法后 Plupload 会立刻中断本次请求,这时服务器正在处理请求,还未及时将处理结果返回至前端请求被中断了,造成服务器实际已保存该文件块,但 Plupload 认为该文件块未上传完成,导致暂停后重新开始,服务端文件块重复,服务端接收到的文件与实际不同。在本文中我将会解决这个问题。
Plupload 获取
正确的途径当然要从官网下载
本人下载版本 Plupload 2.3.6 AGPLv3,里面包含需要用到的 js,还有 Custom example 和 Events example 两个 Demo 页面。接下来将改写 Custom example 页面,实现需要的功能。
前端 HTML 页面
前端使用比较简单,通过一些简单参数配置即可使用,具体参数设置可查看官方文档,其中主要用到 Plupload 插件的 start() 和 stop() 两个方法
修改后的 Custom example 如下,附上源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
Plupload - Custom example
Custom example
Shows you how to use the core plupload API.
[Select files]
[Upload Start]
[Upload Pause]
// Custom example logic
var uploader = new plupload.Uploader({
runtimes: 'html5,flash,silverlight,html4',
browse_button: 'pickfiles', // you can pass an id...
container: document.getElementById('container'), // ... or DOM Element itself
url: 'http://127.0.0.1:8001/demo/upload', //服务器端上传处理程序的URL
flash_swf_url: '../js/Moxie.swf',
silverlight_xap_url: '../js/Moxie.xap',
chunk_size: '2mb', //分块大小
unique_names: true, //生成唯一文件名
send_file_name: true, //发送文件名
max_retries: 3, //在触发Error事件之前重试块或文件的次数
// multipart_params: { gmtCreate : new Date().getTime() }, //每次上传文件时要发送的键/值对的哈希值
filters: {
max_file_size: '4096mb',
mime_types: [
// {title : "Image files", extensions : "jpg,gif,png"},
// {title : "Zip files", extensions : "zip"},
// {title : "Video files", extensions : "mp4"}
{title: "All files", extensions: "*"}
]
},
init: {
PostInit: function () {
// uploader.setOption('multipart_params', {gmtCreate: new Date().getTime()});
document.getElementById('filelist').innerHTML = '';
document.getElementById('start').onclick = function () {
uploader.start();
return false;
};
document.getElementById('stop').onclick = function () {
uploader.stop();
return false;
};
},
FilesAdded: function (up, files) {
plupload.each(files, function (file) {
document.getElementById('filelist').innerHTML += '
';
});
},
UploadProgress: function (up, file) {
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = '' + file.percent + "%";
},
ChunkUploaded: function (up, file, info) {
// Called when file chunk has finished uploading
console.log('[ChunkUploaded] File:', file, "Info:", info);
},
Error: function (up, err) {
document.getElementById('console').appendChild(document.createTextNode("\nError #" + err.code + ": " + err.message));
}
}
});
uploader.init();
服务端,以 Spring Boot 1.5.14.RELEASE 为例
Plupload 对象
Plupload 上传文件时会附带 name, chunks, chunk 三个参数,这里我通过 Plupload 对象接收参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71/**
* Plupload 实体类,建议不要修改
*
* @author Leaves
* @version 1.0.0
* @date 2018/7/26
*/
public class Plupload{
/**
* 文件名
*/
private String name;
/**
* 上传文件总块数
*/
private int chunks = -1;
/**
* 当前块数,从0开始计数
*/
private int chunk = -1;
/**
* 上传时间戳,自定义
*/
private Long gmtCreate;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getChunks(){
return chunks;
}
public void setChunks(int chunks){
this.chunks = chunks;
}
public int getChunk(){
return chunk;
}
public void setChunk(int chunk){
this.chunk = chunk;
}
public Long getGmtCreate(){
return gmtCreate;
}
public void setGmtCreate(Long gmtCreate){
this.gmtCreate = gmtCreate;
}
@Override
public String toString(){
return "Plupload{" +
"name='" + name + '\'' +
", chunks=" + chunks +
", chunk=" + chunk +
", gmtCreate=" + gmtCreate +
'}';
}
}
upload(),接收 Plupload 上传数据
本文提到上传暂停后重新开始,Plupload 会重复上传上次最后一块文件数据这一问题,这里给出一种解决办法:每次收到 Plupload 上传的文件块,处理成功后将当前文件块编号记录到缓存(缓存需要设置过期时间,防止永久驻留),暂停后重新开始,服务端比较本次上传文件块是否已在上次上传处理,如果已处理则抛弃改文件块。
如果需要支持文件秒传,可以通过验证文件 MD5 值简单实现,Java 可通过 commons-codec 包下的 DigestUtils.md5Hex(new FileInputStream(filePath))); 方法获取文件 MD5 值,该 Demo 未做文件秒传。
具体实现,请看源码:
/**
* 文件上传,断点续传,分块上传
*
* @param request
* @param response
* @param plupload
* @return
*/
public boolean upload(HttpServletRequest request, HttpServletResponse response, Plupload plupload){
//按日期文件夹保存
Calendar calendar = Calendar.getInstance();
File serverDir = new File(System.getProperty("user.dir") + File.separator
+ "uploads" + File.separator
+ calendar.get(Calendar.YEAR) + File.separator
+ (calendar.get(Calendar.MONTH) + 1));
//mkdirs() 可创建多级目录,mkdir() 只能创建一级目录
if (!serverDir.exists()) {
if (serverDir.mkdirs()) {
//文件夹创建失败返回403
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}
//文件名
String fileName = plupload.getName();
//上传文件总块数
int chunks = plupload.getChunks();
//当前上传块,从 0 开始
int nowChunk = plupload.getChunk();
//文件块重复检查
ValueOperations operations = redisTemplate.opsForValue();
Object lastChunk = operations.get("UPLOAD_" + plupload.getName());
if (lastChunk != null) {
if (Objects.equals(Integer.parseInt(lastChunk.toString()), nowChunk)) {
logger.warn("文件块重复,chunks: {}, now chunk: {}, last chunk: {}", chunks, nowChunk, lastChunk);
return true;
}
}
//获取文件
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
MultiValueMap map = multipartHttpServletRequest.getMultiFileMap();
if (map == null || map.size() <= 0) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
for (String key : map.keySet()) {
List multipartFileList = map.get(key);
File targetFile = new File(serverDir + File.separator + fileName);
for (MultipartFile multipartFile : multipartFileList) {
try {
//上传文件总块数 > 1,则为分块上传,需要进行合并
if (chunks > 1) {
this.writePartFile(multipartFile.getInputStream(), targetFile, nowChunk != 0);
} else {
//上传文件总块数 = 1,直接拷贝文件内容
multipartFile.transferTo(targetFile);
}
} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false;
}
}
}
//记录上传块数
if (nowChunk == chunks - 1) {
redisTemplate.delete("UPLOAD_" + plupload.getName());
} else {
operations.set("UPLOAD_" + plupload.getName(), String.valueOf(nowChunk), 86400L, TimeUnit.SECONDS);
}
return true;
}
/**
* 分块写入
*
* @param inputStream
* @param file
* @param append
*/
private void writePartFile(InputStream inputStream, File file, boolean append){
OutputStream outputStream = null;
try {
if (!append) {
//从头开始写
outputStream = new BufferedOutputStream(new FileOutputStream(file));
} else {
//追加写入
outputStream = new BufferedOutputStream(new FileOutputStream(file, true));
}
byte[] bytes = new byte[1024];
int len = 0;
while ((len = (inputStream.read(bytes))) > 0) {
outputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
断点续传效果图
plupload断点续传 php,Plupload 文件断点续传,文件分块上传相关推荐
- ssm上传文件进度条_SSM框架+Plupload实现分块上传大文件示例
关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了.Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务 ...
- Python实现GCS bucket断点续传功能,分块上传文件
Python实现GCS bucket断点续传功能,分块上传文件 环境:Python 3.6 我有一个关于使用断点续传到Google Cloud Storage的上传速度的问题.我已经编写了一个Pyth ...
- SSM框架+Plupload实现分块上传(Spring+SpringMVC+MyBatis+Plupload)
关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了.Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务 ...
- java 分块上传_Java 文件分块上传客户端和服务器端源代码
本博客介绍如何进行文件的分块上传.本文侧重介绍客户端,服务器端请参考博客<Java 文件分块上传服务器端源代码>.建议读者朋友在阅读本文代码前先了解一下 MIME 协议. 所谓分块上传并非 ...
- js php 分段上传文件,php+js实现文件分块上传
咱们在上传大文件时,可能会因为服务器的缘由致使文件上传失败,文件过大时因为服务器的配置或响应事件过长致使上传文件失败,这时候咱们能够将一个大的文件分为若干块,而后分批次上传到服务端,当全部文件块上传完 ...
- 【工作笔记】Dropzone实现文件分块上传
文章目录 一.问题与思路 1.1 开发过程中的附件问题 2.2 原因与思路 二.解决过程 2.1 Dropzone使用介绍 2.2 前端代码参考 (1)网上的例子(项目是PHP) (2)项目(后端为J ...
- Http文件分块上传
注: 这里只是讨论一种文件分块上传的方案(解决方案并不唯一) 1.需要服务端支持 如何通知服务端,客户端对于上传文件分了几片? 如何通知服务端,当前为上传文件的第几片? 可将以下数据放到post请求的 ...
- ftp服务器上传文件不行,ftp服务器上传文件不行
ftp服务器上传文件不行 内容精选 换一换 本文介绍如何在 Linux 系统的本地机器上使用 FTP 服务,将文件从本地上传到云服务器中.已在待上传文件的云服务器中搭建 FTP 服务.如果您的云服务器 ...
- 个人上传文件进服务器,个人上传文件进服务器
个人上传文件进服务器 内容精选 换一换 安装传输工具在本地主机和Windows云服务器上分别安装数据传输工具,将文件上传到云服务器.例如QQ.exe.在本地主机和Windows云服务器上分别安装数据传 ...
最新文章
- Java 8 中 Date与LocalDateTime、LocalDate、LocalTime互转
- 【华科考研机试题】阶乘
- Git 使用帮助(下)
- Spring的IOC底层实现
- Linux监控命令之 top
- matlab实现双边滤波_【他山之石】pytorch 实现双边滤波
- 1947-2020 NBA总冠军次数排行榜
- Ubuntu 20.04 LTS 开发周期的重要任务:移除 Python 2
- python包裹和运费_使用shopifyapipython,添加新产品并注明价格和“需要运费”:Fals...
- Windows/Ubuntu 使用小技巧记录
- 一阶惯性传感器的快速跟踪性能实现
- Linux user 的密码策略设定 /etc/shadow
- 项目管理六大制约因素_项目管理有哪些主要风险及如何控制
- mysql the cabinet_mysql 一个较特殊的问题:You can’t specify target table ‘wms_cabinet_form’ | 很文博客...
- python查询12306余票_Python爬虫----12306火车票余票查询器
- RK3399 Android 7.1开发准备
- 一个花里胡哨的渐变雷达图 echarts图表
- vue从数据库获取图片地址,为什么图片地址为变量时找不到图片?
- 清华四年,我学到了什么?
- UE4 error C7525: 内联变量至少需要 “/std:c++17“
热门文章
- Domino V11 Restart方案和授权模式
- form表单提交List集合
- PMP备考建议这些点一定要记住
- #线段树,猫树#SP1043 GSS1 SP1716 GSS3 SP2916 GSS5
- 菜鸟的蜕变:教你一步一步创建基于laravel5的简易论坛系统(3)
- 第八章 使用Spring Web Flow
- 人工智能系统的业务架构
- JDK是什么?JDK包含哪些内容?
- 未来软件测试行业发展的10大趋势,就业钱景
- oracle 10 awr,其它 - Oracle 10g AWR Report 分析_数据库技术_Linux公社-Linux系统门户网站...