在编写实现“天云图床”这个程序的时候,想要实现批量上传图片的操作,便于自己和广大站长的使用,其中遇到了一些简单但又不常见的问题,网上资料良莠不齐,遂总结一遍
一开始,天云图床只有单图上传的功能,开源到技术QQ群中,大家都建议小东写一个多图上传的功能,于是趁着周六有时间,开始编写代码。
本以为是一个非常简单的工作,哪知道弄了一整天的时间。
0x01一开始的思路
一开始,后台API服务仅支持单图上传,想着,不动服务器的代码,尝试从客户端来实现。
开发前的环境:
新浪图床服务端仅支持单图(form表单形式)上传图片即可 前端(客户端 https://api.top15.cn/picbed/)使用 ajax,实例 form表单 内容,发送给服务端,服务端接受数据,读取$_FILES中的文件 服务端(PHP),读取post过来的文件,然后模拟POST远程上传新浪服务器,新浪图床的接受参数为`base64`编码过的图片资源。
0x02 客户端的问题
在这之前,参考了一些开源项目,比如 ssi-uploader
、zuypload
这些基于 jquery
的三方插件,他们的共同功能是,能够读物本地文件,并将本地图片展示在前端页面上。
在站长之家的网站上找到如上的多图上传案例,支持拖拽和多图上传,想着自己是否可以尝试写一下呐?
首先客户端中 HTML
代码需要注意 input
的 name
属性值要为数组,否则上传了数据,在服务端值会接受一个文件数据
如下是我在实现过程中的方式,并设置了隐藏,因为本身 input
文件组件不够美观,用另外的按钮来控制此组件。
<form id="form" enctype="multipart/form-data"> <input type="file" name="files[]" multiple="multiple" id="files" accept="image/png,image/gif,image/jpg,image/jpeg" style="display: none;"> </form>
与此同时,想要实现读取 files
中的图片并显示在界面上,那么就需要监听 input[type=file]
组件的变换
<script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="javascript/text"> $(function(){ $("#files").change(function(){ console.log($("#files")[0].files) //得到一个FileList对象 }) }) </script>
上述代码在控制台打印上传文件的信息,可以看到如下情况:
在files
中,包含了文件的大小,名字,时间和相对路径
0x03 如何展示预览图片?
在 HTML5
中有一个 FileReader()
的东西,能够读取文件内容,
// javascript function loadImg(){ //获取文件 var file = $("#files")[0].files[0]; //创建读取文件的对象 var reader = new FileReader(); //创建文件读取相关的变量 var imgFile; //为文件读取成功设置事件 reader.onload=function(e) { imgFileUrl = e.target.result; $("#gallery").append('<img src="'+imgFileUrl+'" width="200px" height="200px">'); }; reader.readAsDataURL(file); } ////////////////////////////////////////////////////////////// FileReader对象的方法 方法名 参数 描述 readAsBinaryString file 将文件读取为二进制编码 readAsText file,[encoding] 将文件读取为文本 readAsDataURL file 将文件读取为DataURL abort (none) 终端读取操作
在控制台中执行上述代码,然后运行 loadImg()
函数,即可打印准备上传文件中的第一张图片内容到控制台,类似这样的字符串 data:image/png;base64,iVBORw0KGgoAAAANSUhEU...
,这是将图片进行了base64
编码,服务端只需要解码base64
即可恢复原文件。将内容复制浏览器的地址栏打开是没法儿预览的,小东现在想了一下,其原因主要是:浏览器地址栏,类似GET请求,限制了URL的长度和大小,故无法正常显示图片(此处浪费了较多时间,一直以为读取出来的数据不对!!!其实是完全正确的)。
然后本地写了一个测试文件,如下:
不同文件的数据头部是不一样的:data:image/png;base64
,这是上传了一个 png
图片的案例,上传 jpeg
,gif
等等头部都会改变,真正有效的是,
(逗号)后面的内容,才是文件内容的编码,客户端直接提交整个参数即可无需处理,处理交给服务端即可。
服务端支持一个图片上传,那么我们循环读取,然后上传即可,至于返回的数据,以json
方式传输,将单个上传信息直接array_push()
到一个数组即可。
服务端代码的改写(部分):
<?php $result = []; foreach ($_POST as $key => $value) { $file = $value; //BASE64 $cookie = $config['cookie']; $file = explode(',', $file); $imgarr = upload($file[1], $type, $cookie); //TYPE是GET得到,值为bs64,避免post内容混淆 array_push($result, $imgarr); } echo json_encode($result); exit(); ?>
至此,客户端 base64
版本及服务端改造完成。
0x04 FormData版本
在Javascript
中,有Jquery
中的 FormData
方法,使得 Form
表单以 ajax
方法传输变得可行。
博主个人觉得这种方式更好,代码具有简洁性,同时后端按照传统方式来处理
前端代码(javascript
):
<script type="text/javascript"> $(function(){ //选择图片 $('.btn-primary').click(function(){ $('#files').trigger('click'); }) $('#files').change(function(){ var fileNum = $("#files")[0].files.length; $('.btn-primary').text('上传文件('+fileNum+')'); }) //点击上传图片 $('#upload_btn').click(function(){ $("#upload_btn").text("上传中..."); $('#upload_btn').attr('disabled', true); var fileNum = $("#files")[0].files.length; if(fileNum && fileNum <= 10){ $.ajax({ url : "https://api.top15.cn/picbed/picApi.php?type=multipart&num=multiple", //此API可供大家免费使用,且行且珍惜 type : 'POST', cache: false, data : new FormData($('#form')[0]), processData : false, contentType : false, async : false, success : function(data) { if(data){ var str = ''; var str2 = ''; $.each(data, function(index, obj){ str += '<a href="'+ obj.url2+'" target="_blank">'+obj.url2+'</a><br>'; str2 += '<img src="'+obj.url2+'" width="'+obj.width+'" height="'+obj.height+'">'; }) $(".imgs_src").html(str); $(".textarea").text(str2); } } }); $("#upload_btn").attr('disabled', false); $('#files').val(''); $('.btn-primary').text('上传文件(0)'); } else{ alert("请选择上传文件,且上传文件数不超过10!"); $('#files').val(''); $('.btn-primary').text('上传文件(0)'); $("#upload_btn").attr('disabled', false); } $("#upload_btn").text("一键上传"); }) }) </script>
后端处理:
<?php $result = []; foreach ($_FILES['files']['tmp_name'] as $key => $value) { //从$_FILES中读取文件 $file = $value; $cookie = $config['cookie']; $imgarr = upload($file, $type, $cookie); array_push($result, $imgarr); } echo json_encode($result); exit(); ?>
0x05 最终完成:
完成项目的展示网站:https://api.top15.cn/picbed/
开源地址:https://github.com/dyboy2017/sinaPictureBed
0x06 总结
本次程序实现中,input
控件中的 name
属性值要为数组,其次 HTML5
功能 FileReader()
是一个不错的方法,有利于及时展示即将上传的图片,一些多图上传的插件,使用的就是此种方式,但是会耗费客户端较多的计算资源,可能导致客户端的卡顿(可以限制上传图片的大小以及数量是一种解决方案),简单的直接使用 Jquery
中的 FormData()
即可使用 ajax
异步传输数据,最后测试,发现在 Android
手机浏览器上还是无法多图上传,但是在 Iphone
手机上可以多图上传,在PC端,360浏览器运行该页面会卡顿但不影响正常操作,在 Firfox
浏览器上表现正常!HTML5
特性在需要的查询文档使用即可,
版权声明:《 HTML多图上传中的问题探索 》为DYBOY原创文章,转载请注明出处!
最后编辑:2019-2-25 13:02:25
2019-02-26 00:45