最近在做上传的功能,奈何Jquery都忘记得差不多了,只能百度找文档,然后一步步实现了。目前做的是本地上传,上传到七牛的还没弄到。以后要上传到七牛了再补上。
页面效果图如下。
主要功能点:
1.上传图片。
2.可以预览。
3.上传到本地。
4.再次打开时从数据库获取图片。
5.再次上传时,根据是否有删除的图片,判断是要进行删除还是上传。
代码展示:
1.上传图片
需要用到input[type='file']。由于是要弄成上图这样的效果,所以不能用多个input框,而是只使用了一个input框,css将其透明度改为0,然后放在一个div里面。如图显示的加号。整体布局采用的是float布局,这样在新增图片了之后就可以显示在+号的旁边了。
上传图片需要注意的是input要accept image/*,之前听说 * 会导致卡顿,不过现在好像又没事了。
<div class="add-photo">
<div class="upload-files">
</div>
<div class="upload-box">
<input class="upload-input" name="packImg[]" multiple="multiple" type="file"
accept="image/gif,image/jpeg,image/jpg,image/png" hidden="true">
</div>
</div>
2.预览功能
这里主要是对input进行监听,绑定change事件。
这里有个坑,就是删除了刚选择的图片,然后再点击上传后,不会触发change事件,所以在触发了change后,需要手动设置input的值为空。
然后预览需要用到的是FileReader这个API。用法也非常的简单。
创建一个FileReader的实例,然后通过fileReader.readAsDataURL(file)来读取文件,最后监听fileReader的onload事件。就能得到图片的地址了。最后再讲地址渲染到网页上即可。
这里我创建的是一个Img类,里面包含了增删渲染和变成formData的方法。主要是为了统一管理。
在添加的时候需要判断是否已经包含了该文件,如果有的话就不再渲染该图片,没有的话则将其数据放入到files中。
//图像类
class ImgFile {
constructor() {
this.files = [];
this.deletePic = [];
this.dom = $('.upload-files');
this.needUpload = false;
}
addUploadedFile(files) {
for (let obj of files) {
this.files.push(obj);
this.createImg(obj.url);
}
}
addFile(files) {
for (let i = 0; i < files.length; i++) {
let file = files[i];
let isNew = true;
for (let j = 0; j < this.files.length; j++) {
if (this.files[j].name === file.name) {
isNew = false;
break;
}
}
if (isNew) {
this.needUpload = true;
this.files.push(file);
this.renderFile(file);
}
}
}
// 渲染页面
renderFile(file) {
let fileReader = new FileReader();
fileReader.readAsDataURL(file);
let that = this;
fileReader.onload = function (e) {
let src = e.target.result;
that.createImg(src);
}
}
// 创建图片
createImg(src) {
let imgDom = `
<div class="upload-file">
<img class="upload-img" src="${src}" alt="">
<span class="upload-delete">×</span>
</div>
`;
this.dom.append($(imgDom));
}
// 删除图片
removeFile(idx) {
if (!(this.files[idx] instanceof File)) {
this.deletePic.push(this.files[idx].id)
this.needUpload = true
}
;
this.files.splice(idx, 1);
if (this.files.length === 0 && this.deletePic.length === 0) {
this.needUpload = false;
}
}
getFormData() {
let formData = new FormData();
for (let i = 0; i < this.files.length; i++) {
let file = this.files[i]
if (file instanceof File) {
formData.append('images[]', file);
}
}
return formData;
}
clear() {
this.files = [];
this.deletePic = [];
this.needUpload = false;
$('.upload-files').html('');
}
}
3.上传到本地
因为不能按常规的方法,通过form来进行上传,所以我们要自己来创建一个formdata,然后通过ajax来进行上传。
getFormData() {
let formData = new FormData();
for (let i = 0; i < this.files.length; i++) {
let file = this.files[i]
if (file instanceof File) {
formData.append('images[]', file);
}
}
return formData;
}
该方法存在于类中,将所有的文件对象全部放入formData中,然后进行上传。因为是多张图片,所以formdata中的该字段要用[]来表示。
若有其他需要上传的字段,则也一并放入formData中。通过formData的append方法来进行实现。
PHP部分:
由于TP5.1自带了图片上传的方法,所以也是特别的简单。我们只需要拿到图片,然后通过遍历循环放到本地即可。
//获取图片
$files = request()->file('images');
foreach ($this->files as $file) {
// 移动到框架应用根目录/uploads/ 目录下
$rootPath = Env::get('root_path');
$info = $file->move($rootPath . 'public' . DIRECTORY_SEPARATOR . 'static/uploads');
if ($info) {
// 保存路径地址,循环完毕后上传到数据库
$uploadStatus['pathArr'][] = ['url' => $info->getSaveName(), 'source' => ImageSource::LOCAL];
} else {
// 上传失败获取错误信息
$uploadStatus['pass'] = false;
}
}
4.再次打开时从数据库获取图片。
就是一个读取操作,就不多说了。
只是读取完返回数据的时候,需要将地址记录到ImgFile这个实例当中。this.files.push(src)
5.再次上传时,根据是否有删除的图片,判断是要进行删除还是上传。
在ImgFile这个实例中,删除图片,然后将图片的id记录下来,在点击确定时,判断是否需要发送给服务器进行删除操作。
有则进行数据库删除操作。发送的都是和上传图片时候同一个API地址。
// 删除图片
removeFile(idx) {
if (!(this.files[idx] instanceof File)) {
this.deletePic.push(this.files[idx].id)
this.needUpload = true
}
;
this.files.splice(idx, 1);
if (this.files.length === 0 && this.deletePic.length === 0) {
this.needUpload = false;
}
}
主要的就是这些,假如以后涉及上传到云上传的话再进行更新~