在编写实现“天云图床”这个程序的时候,想要实现批量上传图片的操作,便于自己和广大站长的使用,其中遇到了一些简单但又不常见的问题,网上资料良莠不齐,遂总结一遍
一开始,天云图床只有单图上传的功能,开源到技术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内容在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的长度和大小,故无法正常显示图片(此处浪费了较多时间,一直以为读取出来的数据不对!!!其实是完全正确的)。
然后本地写了一个测试文件,如下:
base64不同文件的数据头部是不一样的: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
特性在需要的查询文档使用即可。
网友评论