最近老大分给我了做一个电影cms系统,其中涉及到一个功能,使用七牛云的文件上传功能。七牛javascript skd,使用起来很方便,屏蔽了许多的技术细节。如果只满足与调用sdk,那么可能工作中也就没有什么收获了。其中对七牛云的服务很佩服的一点是,无论我上传多大的文件,当我文件最后一片上传完成的时候,就立刻返回到文件链接,这个问题我想了好久,也不知道七牛是如何做到的。

七牛云的sdk分为 JavaScript 与 PHP端。 JavaScript端的作用是提供文件上传功能,客户端(浏览器)无需关注当前的环境,七牛云的sdk会自动检测浏览器的版本,并提供统一的接口调用。PHP端主要作用是提供鉴权的,每次七牛云上传图片到七牛云的空间,需要到自己的应用服务器,拿到一个授权的key,你可以理解为密钥。

关于七牛云 JavaScript 与 PHP端的 SDK 使用,本篇就不赘言了,官网的文档描述的很清楚,可以参考 七牛云javascript sdk 文档

七牛云的JavaScript SDK 继承了 plupload 的所有方法。本篇的主要目的是展示plupload的使用场景,以及文件分片上传后,后端服务器如何处理,提供一个思路。

如果你有以下的业务场景,可以尝试使用plupload 插件

1、用户上传图片,需要实时预览,并且兼容主流浏览器
2、上传的图片需要在本地进行质量压缩,或者文件类型校验
3、上传图片的时候,需要提示上传的百分比
4、客户端可能需要上传较大的文件,但是服务端的配置并不允许开启大文件上传
5、需要断点续传功能,即文件上传了一半,下次上传时可以接着上传。

前端的代码和后端PHP的代码放在文后,代码并不重要,思路最重要。

前端将文件分成片后,浏览器开启多线程上传服务。例如将一个文件分成100片,由于异步的原因,可能第1片 和 第10片 先到,因而分片的上传你可以理解为乱序的。另外php上传文件是上传到临时文件夹,当脚本执行结束后,就会自动删除文件。所以如果不对分片上传的数据进行保存,那么就会竹篮打水一场空,等所有分片都上传完了,结果却无法合并。因此我们后端需要解决几个问题:

1、将分片保存起来
2、自己维护分片的顺序
3、需要将分片合并到最终的目标文件中
4、当分片合并后,需要删除无用的分片

维护分片的这部分解决,我使用了Reids 的Zset 数据结构,该结构能帮我解决分片的顺序。

分片上传的时候,会标注共有多少分片, 这是第几个分片.

后端PHP代码:

<?php
/**
* 这是个上传测试文件,作为研究分片上传原理使用
*
*/ if (!is_array($_FILES) || empty($_FILES)) {
die(); //输出报错的话术
} $destication = "/usr/local/var/www/uploads/"; //上传文件的最终文件夹
$destication_frag_path = "/usr/local/var/www/uploads_tmp/"; //分片上传的临时文件夹 if (!is_dir($destication) || !is_dir($destication_frag_path)) {
@mkdir($destication, 0755);
@mkdir($destication_frag_path, 0755);
} if ($_REQUEST['chunks'] == 1) {
//文件很小,无需分片上传。
$tmp_file_path = $_FILES['file']['tmp_name']; //上传的临时文件
$save_file_name = $destication.$_REQUEST['name'];
move_uploaded_file($tmp_file_path, $save_file_name);
} else {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis_key = $_REQUEST['name']; $file_name = explode('.', $_REQUEST['name']);
$save_tmp_name = $destication_frag_path.$file_name[0]."_".$_REQUEST['chunk']; //文件名拼接成第几块
$tmp_file_path = $_FILES['file']['tmp_name']; //上传的临时文件
move_uploaded_file($tmp_file_path, $save_tmp_name); $redis->setTimeout($redis_key, 3600); //一个小时后过期
$redis->zAdd($redis_key, $_REQUEST['chunk'], $save_tmp_name);
$uploaded_count = $redis->zCard($redis_key); //分片资源上传完毕后,开始分片合并工作
if ($uploaded_count == $_REQUEST['chunks']) { //获取经过排序后的分片资源
$all_files_fen_pian = $redis->zRange($redis_key, 0, -1); if ($all_files_fen_pian && is_array($all_files_fen_pian)) { //创建要合并的最终文件资源
$final_file = $destication.$_REQUEST['name'];
$final_file_handler = fopen($final_file, 'wb'); //开始合并文件分片
foreach ($all_files_fen_pian as $fragmentation_file) {
$frag_file_handler = fopen($fragmentation_file, 'rb');
$frag_file_content = fread($frag_file_handler, filesize($fragmentation_file));
fwrite($final_file_handler, $frag_file_content); unset($frag_file_content);
fclose($frag_file_handler); //销毁分片文件资源
unlink($fragmentation_file); //删除已经合并的分片文件
usleep(10000);
}
}
}
}

前端Javascript 代码:


var uploader = new plupload.Uploader({
browse_button : 'browse', //触发文件选择对话框的按钮,为那个元素id
url : 'upload.php', //服务器端的上传页面地址
flash_swf_url: './public/js/plupload/js//Moxie.swf', //flash文件地址
max_file_size: '1000mb',//限制为2MB
chunk_size:'1mb',
unique_names:true, //为每个文件生成一个临时名称
max_retries:3,
multipart_params:{
},//扩展参数
//filters: [{title: "image ",extensions: "jpg,gif,png"}], //图片限制
//filters: [{title: "movie ",extensions: "mp4"}], //电影限制
silverlight_xap_url : 'js/Moxie.xap' //silverlight文件,当需要使用silverlight方式进行上传时需要配置该参数
}); uploader.init(); //初始化uploader
uploader.start(); //开始上传
uploader.stop(); //暂停上传 $("#start_upload").click(function(){
uploader.start();
}); //文件添加的时候
uploader.bind('FilesAdded', function (uploader, files) {
/**
* 因此在这一步可以判断文件的上传个数,和文件的格式,以及上传文件的大小,以及进行图片预览的相关东东
*/
console.log('-----当文件添加的时候打印 开始----');
for (i=0; i<files.length; i++ ) {
var tmp_file_size = files[i]['size'] / 1024;
var tmp_msg = '添加文件的索引:'+ files[i]['id']+ "\t\t原始文件名:"+files[i]['name'] + "\t文件后缀:"+files[i]['type']+"\t文件的大小:"+tmp_file_size+"kb";
console.log(tmp_msg);
}
// 如果是图片的时候可以启用这部分
// mOxie.each(files, function(files) {
// var image = new mOxie.Image();
// image.onload = function() {
// var dataUrl = image.getAsDataURL();
// $("#preview_img").attr('src', dataUrl);
// };
//
// image.load(files.getSource());
// }); console.log('-----当文件添加的时候打印 结束----');
}); //当上传队列中某一个文件开始上传后触发。
uploader.bind('BeforeUpload', function(uploader, file){
console.log('文件开始上传了');
//console.dir(file);
}); //当使用文件小片上传功能时,每一个小片上传完成后触发
uploader.bind('ChunkUploaded', function(uploader,file,responseObject){
console.log('-----当文件上传分片的时候打印 开始----');
//console.dir(JSON.parse(responseObject['response']));
console.log('-----当文件上传分片的时候打印 结束---');
}); //会在文件上传过程中不断触发,可以用此事件来显示上传进度
uploader.bind('UploadProgress', function(uploader,file) {
//console.log(uploader);
//console.log(file);
}); //当队列中的某一个文件上传完成后触发
uploader.bind('FileUploaded',function(uploader,files,data){
console.log('-----当文件上传完成的时候打印 开始----');
//console.dir(files);
console.dir(data);
console.log('-----当文件上传完成的时候打印 结束----');
}); //当上传队列中所有文件都上传完成后触发
uploader.bind('UploadComplete', function(uploader,files) {
console.log('所有的文件都已经上传完毕了');
}); //当上传发声错误时触发
uploader.bind('Error', function(uploader,errObj) {
console.log('-----当文件上传错误的时候打印 开始----');
console.dir(errObj);
console.log('-----当文件上传错误的时候打印 结束----');
}); </script>

注意事项: 本代码仅作为研究分片上传的原理使用,未应用于生产环境

本文代码github 地址: roverliang 的github

plupload 学习的相关地址:

  1. pluplod github 地址
  2. pluplod 官网(可能要翻墙)
  3. pluplod 中文文档

plupload 大文件分片上传与PHP分片合并探索的更多相关文章

  1. Html5 突破微信限制实现大文件分割上传

    先来前端代码 <!DOCTYPE html> <html> <head> <meta name="viewport" content=&q ...

  2. js大文件分块上传断点续传demo

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

  3. PHP实现大文件的上传设置

    打开php.ini,首先找到 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; 区域,有影响文件上传的以下几个参数: file_uploads = ...

  4. PHP + JS 实现大文件分割上传

    服务器上传文件会有一定的限制.避免内存消耗过大影响性能,在 php.ini 配置文件中,有几个影响参数: upload_max_filesize = 2M //PHP最大能接受的文件大小 post_m ...

  5. Hadoop如何将TB级大文件的上传性能优化上百倍?

    这篇文章,我们来看看,Hadoop的HDFS分布式文件系统的文件上传的性能优化. 首先,我们还是通过一张图来回顾一下文件上传的大概的原理. 由上图所示,文件上传的原理,其实说出来也简单. 比如有个TB ...

  6. Ajax+Java实现大文件切割上传

    技术体系:html5(formdata) + java + servlet3.0+maven + tomcat7 <!DOCTYPE html> <html> <head ...

  7. formdata方式上传文件,支持大文件分割上传

    1.upload.html <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/html"> <h ...

  8. PHP大文件分割上传(分片上传)

    服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关 upload_max_filesize = 2M //PHP最大能接受的文件大小 post_max_size = 8M //PHP能收 ...

  9. PHP实现大文件分割上传与分片上传

    转载:http://www.zixuephp.com/phpstudy/phpshilie/20170829_43029.html 服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关 u ...

随机推荐

  1. Java 8 新特性-菜鸟教程 (1) -Java 8 Lambda 表达式

    Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性. Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中). 使用 Lambda 表达式可以使代码变的更加 ...

  2. (转)mybatis热加载(依赖mybatis-plus插件)的实现

    最近在使用mybatis,由于是刚刚开始用,用的并不顺手,目前是感觉有2个地方非常的不好用: 1.mybatis调试不方便 由于dao层只有接口,实现只是一个map的xml文件,想加断点都没有地方加, ...

  3. ubuntu 上安装ssh

    1. 执行 sudo apt-get update 2. 安装 sudo apt-get install openssh-server 3.查看ssh服务状态 sudo service ssh sta ...

  4. Hibernate映射 --- 自身跟自身的一对多关联

      自身跟自身的一对多关联 什么时候会出现自身一对多关系呢?下面举个例子. 淘宝店里商品分类,一级分类:家用电器,个人化妆,运动户外等 家用电器下面二级分类:大家电,生活电器,厨房电器等 二级分类大家 ...

  5. 如何给oracle账户解锁

    在创建数据库时,已经为SYS等4个账户设定了口令,其中SYS与SYSTEM具有管理员权限,在SQL*Plus工具中使用SYSTEM账户登录Oracle数据库. 1.通过数据字典dba_users,查看 ...

  6. Android四大组件-Broadcast Receiver

    http://www.jianshu.com/p/1013a366cc72 http://www.jianshu.com/p/ca3d87a4cdf3 前言 基础知识. 1.Android广播分为两个 ...

  7. Java - 异常解析基础

    java提高篇(十六)-----异常(一) 一.为什么要使用异常 首先我们可以明确一点就是异常的处理机制可以确保我们程序的健壮性,提高系统可用率.虽然我们不是特别喜欢看到它,但是我们不能不承认它的地位 ...

  8. EF数据库优先模式(三)

    今天2018年4月1日,呼叫王伟,81192,收到请返航! 接上次说,本节将LINQ以及lambda表达式 LINQ是C#里面针对SQL Server特有的数据访问操作方法,通俗一点说就是类似于写SQ ...

  9. Navicat11全系列激活工具和使用方法

    Navicat特别好使,但是就是得注册,在网上看到了一个激活工具,成功激活了Navicat...工具链接地址是.. https://files.cnblogs.com/files/miantiaoan ...

  10. Codeforces852G(字符串hash)

    G. Bathroom terminal time limit per test:2 seconds memory limit per test:256 megabytes input:standar ...