作者:娇娇jojo
时间:2018年6月19日
一、铺垫
依靠七牛上传图片,其实有很多方法,先说说有哪些方法,以及这些方法各自的优缺点吧(移动端)。
way1:前端只负责选择图片,然后将图片传给服务端,服务端再传给七牛,再将地址返给前端展示。
优:可以控制上传图片到七牛的时机,不浪费七牛的存储空间;
缺:浪费自己服务器的带宽,并且周周转转,耗时长,麻烦。
way2:前端发通知让客户端来上传图片到七牛。
优:前端不需要处理太多东西,只需要给客户端发一个通知。
缺:选一张图片就上传了一张,如果多次选择,会浪费七牛存储空间。
way3:前端集成七牛的jssdk。
优:可以控制上传图片到七牛的时机,不浪费七牛的存储空间;不通过服务端,所以不会消耗服务器带宽;不通过客户端,所以不需要集成javascriptBridge和客户端通信。
缺:路漫漫其修远兮,需要自己去踩坑。。。。
二、七牛 JSSDK
下面正式进入七牛JSSDK:
1、把该引入的文件都引入了。
三个文件:
(1)产品环境:plupload、plupload.full.min.js、qiniu.min.js
(2)开发调试:plupload.dev.js 、moxie.js、qiniu.js
2、初始化uploader 。
var uploader = Qiniu.uploader({ runtimes: 'html5,flash,html4', // 上传模式,依次退化 browse_button: 'pickfiles', // 上传选择的点选按钮,必需,id名 uptoken : '<Your upload token>', // uptoken是上传凭证,由其他程序生成 get_new_uptoken: false, // 设置上传文件的时候是否每次都重新获取新的uptoken // unique_names: true, // 默认false,key为文件名。若开启该选项,JS-SDK会为每个文件自动生成key(文件名) // save_key: true, // 默认false。若在服务端生成uptoken的上传策略中指定了sava_key,则开启,SDK在前端将不对key进行任何处理 domain: '<Your bucket domain>', // bucket域名,下载资源时用到,必需 container: 'container', // 上传区域DOM ID,默认是browser_button的父元素 max_file_size: '100mb', // 最大文件体积限制 flash_swf_url: 'path/of/plupload/Moxie.swf', //引入flash,相对路径 max_retries: 3, // 上传失败最大重试次数 dragdrop: true, // 开启可拖曳上传 drop_element: 'container', // 拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传 chunk_size: '4mb', // 分块上传时,每块的体积 auto_start: true, // 选择文件后自动上传,若关闭需要自己绑定事件触发上传 init: { 'FilesAdded': function(up, files) { // 文件添加进队列后,处理相关的事情 }, 'BeforeUpload': function(up, file) { // 每个文件上传前,处理相关的事情 }, 'UploadProgress': function(up, file) { // 每个文件上传时,处理相关的事情 }, 'FileUploaded': function(up, file, info) { // 每个文件上传成功后,处理相关的事情 // 其中info是文件上传成功后,服务端返回的json,形式如: // { // "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98", // "key": "gogopher.jpg" // } // 查看简单反馈 // var domain = up.getOption('domain'); // var res = JSON.parse(info); // var sourceLink = domain +"/"+ res.key; 获取上传成功后的文件的Url }, 'Error': function(up, err, errTip) { //上传出错时,处理相关的事情 }, 'UploadComplete': function() { //队列文件处理完毕后,处理相关的事情 }, 'Key': function(up, file) { // 若想在前端对每个文件的key进行个性化处理,可以配置该函数 // 该配置必须要在unique_names: false,save_key: false时才生效 var key = "jojojo/" + file.id; //可以加上自己设定好的前缀(归类好了,自己易管理) return key; } } });
三、常见问题
1、uptoken
uptoken由服务端提供。
uptoken(上传凭证)、uptoken_url(获取上传凭证的地址)、uptoken_func(获取uptoken的过程)三个参数只要有一个被设置就ok。
如果提供了多个,优先级:uptoken > uptoken_url > uptoken_func。
推荐使用uptoken,服务端可以设置一些权限,更安全;使用uptoken_url,相当于将这个接口直接暴露出去,存在安全隐患。
uptoken : '<Your upload token>', // uptoken是上传凭证,由其他程序生成uptoken_url: '/uptoken', // Ajax请求uptoken的Url,强烈建议设置(服务端提供),格式:{"uptoken":"<upload token>"}uptoken_func: function(){ // 在需要获取uptoken时,该方法会被调用 // do something return uptoken; },
2、上传单张图片
将multi_selection设置为false就可以了。
multi_selection: false,
但有的webview并不支持这个属性,依旧可以选择多张图片,特殊情况特殊处理。
在FileAdded函数中处理:
'FilesAdded': function(up, files) { plupload.each(files, function(file) { // 针对multi_selection失效时的处理办法:无论选多少个,只留一个 if(up.files.length > 1){ up.removeFile(up.files[0]); //移除多余的文件 } }); },
3、图片加入队列时就实现预览(不是上传完成后)
都知道上传完成后,会获取到图片的一个链接,这样很容易实现展示。但如果想在图片加入队列时就预览图片,稍稍麻烦点,但也不是没有办法。
在FileAdded函数中处理:
'FilesAdded': function(up, files) { plupload.each(files, function(file) { //针对加入队列时,而不是上传完成后才可以预览图片的处理办法 var preloader = new mOxie.Image(); preloader.onload = function() { $('body').append('<img src="'+preloader.getAsDataURL()+'">'); }; preloader.load( file.getSource() ); }); },
4、 Android 自带的 Webview 对JS SDK不支持
Android里点击“选择文件”按钮没有反应,这是Webview 对 JS 不是很支持造成的,解决方法:要求Android客户端引入七牛的Webview ,jar包地址:
https://o8qpi53g2.qnssl.com/Android-AdvancedWebView.jar
使用方法请参考:
private AdvancedWebView mWebView;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (AdvancedWebView) findViewById(R.id.webview); mWebView.setListener(this, this); mWebView.loadUrl("http://jssdk.demo.qiniu.io/"); }
5、取消自动上传
将auto_start设置为false,绑定自定义的事件触发上传。
auto_start: false,
document.getElementById("btn").onclick = function(){ uploader.start(); }
但这其中还是会存在问题的。
比如说,你上传文件后,要把上传后的链接和其他一些数据传给服务端,现在的逻辑应该是把上传给服务器的操作放在FileUploaded里面,这样才能保证拿到链接。
但如果你选定一张图片后,只是改变其他数据的变化,那你每次都要uploader.start()吗?但事实是,只要你选定一张图片后,uploader.start()只会生效一次,后来就不生效了,那放在uploader.start()里的操作就不执行了,这并不是我们想要的结果。
那就做个判断,如果再选择另外一张图片了,就执行uploader.start(),如果一直没再选择另外一张图片,就直接执行和服务端的交互。那问题是,怎么判断别人有没有选择另外一张图片呢?
用标记法。标记一个flag=0(初始状态),选择图片后,在FileAdded里设置flag=1,代表进入新的图片。上传完成后,也就是在fileUploaded里设置flag=0,代表图片上传完成,只要没有新图片加入,flag一直为0,就可以直接执行和服务端的交互。一旦新图片加入队列,flag=1,就可以执行uploader.start()了。
6、限制上传文件的类型
// 可以使用该参数来限制上传文件的类型,大小等,该参数以对象的形式传入,它包括三个属性:filters : { max_file_size : '100mb', prevent_duplicates: true, //是否可以上传重复的图片,true:不能重复,false:可以重复 mime_types: [ {title : "flv files", extensions : "flv"} // 限定flv后缀上传格式上传 {title : "Video files", extensions : "flv,mpg,mpeg,avi,wmv,mov,asf,rm,rmvb,mkv,m4v,mp4"}, // 限定flv,mpg,mpeg,avi,wmv,mov,asf,rm,rmvb,mkv,m4v,mp4后缀格式上传 {title : "Image files", extensions : "jpg,gif,png"}, // 限定jpg,gif,png后缀上传 {title : "Zip files", extensions : "zip"} // 限定zip后缀上传 ] },
6、修改上传文件命名
将unique_names和save_key都设置为false,并在Key函数中做相关处理。
unique_names: false,save_key: false,
'Key': function(up, file) { var key = "jojojo/" + file.id; //可以加上你的个性前缀(好归类,自己好管理) return key; }
假设domain是:https://www.jojojojo.com
默认得到的文件名是:https://www.jojojojo.com/h53434d8h3jj0
修改之后的文件名是:https://www.jojojojo.com/jojojo/h53434d8h3jj0
修改之后的有了归类,更容易管理。
7、关于多个按钮选择文件的 Demo
很多用户都在问 JSSDK 多文件选择的 Demo,其实只需要在 main.js 文件里多 new 几个 Uploader 对象,然后在主页上写好对应的上传按钮就可以了。
main.js 里面多 new 几个 uploader 对象:
$(function() { var uploader = Qiniu.uploader({ runtimes: 'html5,flash,html4', browse_button: 'pickfiles', container: 'container', drop_element: 'container', max_file_size: '100mb', flash_swf_url: 'js/plupload/Moxie.swf', dragdrop: true, chunk_size: '4mb', uptoken:'um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:79ApUIePTtKIdVGDHJ9D9BfBnhE=:eyJzY29wZSI6ImphdmFkZW1vIiwiZGVhZGxpbmUiOjE0NTk4ODMyMzV9Cg==', // uptoken_url: $('#uptoken_url').val(), //当然建议这种通过url的方式获取token domain: $('#domain').val(), auto_start: false, init: { 'FilesAdded': function(up, files) { $('table').show(); $('#success').hide(); plupload.each(files, function(file) { var progress = new FileProgress(file, 'fsUploadProgress'); progress.setStatus("等待..."); }); }, 'BeforeUpload': function(up, file) { var progress = new FileProgress(file, 'fsUploadProgress'); var chunk_size = plupload.parseSize(this.getOption('chunk_size')); if (up.runtime === 'html5' && chunk_size) { progress.setChunkProgess(chunk_size); } }, 'UploadProgress': function(up, file) { var progress = new FileProgress(file, 'fsUploadProgress'); var chunk_size = plupload.parseSize(this.getOption('chunk_size')); progress.setProgress(file.percent + "%", file.speed, chunk_size); }, 'UploadComplete': function() { $('#success').show(); }, 'FileUploaded': function(up, file, info) { var progress = new FileProgress(file, 'fsUploadProgress'); progress.setComplete(up, info); }, 'Error': function(up, err, errTip) { $('table').show(); var progress = new FileProgress(err.file, 'fsUploadProgress'); progress.setError(); progress.setStatus(errTip); } } }); uploader.bind('FileUploaded', function() { console.log('hello man,a file is uploaded'); }); $('#up_load').on('click', function(){ uploader.start(); }); $('#stop_load').on('click', function(){ uploader.stop(); }); var Q2 = new QiniuJsSDK(); var uploader2 = Q2.uploader({ runtimes: 'html5,flash,html4', browse_button: 'pickfiles2', container: 'container2', drop_element: 'container2', max_file_size: '100mb', flash_swf_url: 'js/plupload/Moxie.swf', dragdrop: true, chunk_size: '4mb', uptoken:'um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:79ApUIePTtKIdVGDHJ9D9BfBnhE=:eyJzY29wZSI6ImphdmFkZW1vIiwiZGVhZGxpbmUiOjE0NTk4ODMyMzV9Cg==', // uptoken_url: $('#uptoken_url').val(), //当然建议这种通过url的方式获取token domain: $('#domain').val(), auto_start: false, init: { 'FilesAdded': function(up, files) { $('table').show(); $('#success').hide(); plupload.each(files, function(file) { var progress = new FileProgress(file, 'fsUploadProgress'); progress.setStatus("等待..."); }); }, 'BeforeUpload': function(up, file) { var progress = new FileProgress(file, 'fsUploadProgress'); var chunk_size = plupload.parseSize(this.getOption('chunk_size')); if (up.runtime === 'html5' && chunk_size) { progress.setChunkProgess(chunk_size); } }, 'UploadProgress': function(up, file) { var progress = new FileProgress(file, 'fsUploadProgress'); var chunk_size = plupload.parseSize(this.getOption('chunk_size')); progress.setProgress(file.percent + "%", file.speed, chunk_size); }, 'UploadComplete': function() { $('#success').show(); }, 'FileUploaded': function(up, file, info) { var progress = new FileProgress(file, 'fsUploadProgress'); progress.setComplete(up, info); }, 'Error': function(up, err, errTip) { $('table').show(); var progress = new FileProgress(err.file, 'fsUploadProgress'); progress.setError(); progress.setStatus(errTip); } } }); uploader2.bind('FileUploaded', function() { console.log('hello man 2,a file is uploaded'); }); $('#up_load2').on('click', function(){ uploader2.start(); }); $('#stop_load2').on('click', function(){ uploader2.stop(); });
相应的 index.html 文件加入相关按钮:
<div id="container"> <a class="btn btn-default btn-lg " id="pickfiles" style="width:160px" href="#" > <i class="glyphicon glyphicon-plus"></i> <span>选择文件</span> </a> <a class="btn btn-default btn-lg " id="up_load" style="width:160px" href="#" > <span>确认上传</span> </a> <a class="btn btn-default btn-lg " id="stop_load" style="width:160px" href="#" > <span>暂停上传</span> </a> </div><div id="container2"> <a class="btn btn-default btn-lg " id="pickfiles2" style="width:160px" href="#" > <i class="glyphicon glyphicon-plus"></i> <span>选择文件</span> </a> <a class="btn btn-default btn-lg " id="up_load2" style="width:160px" href="#" > <span>确认上传</span> </a> <a class="btn btn-default btn-lg " id="stop_load2" style="width:160px" href="#" > <span>暂停上传</span> </a> </div></div>
以上是我遇见的问题,其他问题可以参考七牛JSSDK官网:
https://developer.qiniu.com/kodo/sdk/1283/javascript#6