在我们日常的项目开发中,图片上传是一个很常见的需求。一些常见的UI框架都已经集成了图片上传组件,然而有时候这些组件并不能满足我们的需求,这时候就需要自己写组件实现上传,这就需要弄懂上传的原理以及里面的知识点。本次我就通过简单的代码,为大家解析图片上传的各个知识点。
1、传统图片上传服务器
前端通过 <input type="file"/>
获取上传文件,然后通过ajax的post(设置type为multipart/form-data)上传后端,后端取到文件后保存至服务器并返回路径。
2、微信jssdk图片上传
调用微信的jssdk
,chooseImage
选择图片,uploadImage
上传图片
3、webuploader图片上传
webuploader最常用于百度ueditor编辑器图片上传。百度内置了webuploader上传插件,我们只需修改相应逻辑即可实现阿里云oss的上传
// 富文本编辑器图片上传
window.UEDITOR_CONFIG['imageUploadService'] = function (context, uploader) {
return {
/**
* 触发fileQueued事件时执行
* 当文件被加入队列以后触发,用来设置上传相关的数据 (比如: url和自定义参数)
* @param {Object} file 当前选择的文件对象
*/
setUploadData: function (file) {
// 这里添加token
getUploadToken(function (ossInfo) {
file.ossInfo = ossInfo
return file
}, file)
},
/**
* 触发uploadBeforeSend事件时执行
* 在文件上传之前触发,用来添加附带参数
* @param {Object} object 当前上传对象
* @param {Object} data 默认的上传参数,可以扩展此对象来控制上传参数
* @param {Object} headers 可以扩展此对象来控制上传头部
* @returns 上传参数对象
*/
setFormData: function (object, data, headers) {
let ossInfo = object.file.ossInfo
let key = ossInfo.dir + '' + randomString(10) + getSuffix(data.name)
data.key = key
data.policy = ossInfo.policy
data.OSSAccessKeyId = ossInfo.OSSAccessKeyId
data.success_action_status = 200
data.signature = ossInfo.signature
let imageList = context.imageList || []
imageList.push({
url: ossInfo.host + '/' + key
})
return data
},
/**
* 触发startUpload事件时执行
* 当开始上传流程时触发,用来设置Uploader配置项
* @param {Object} uploader
* @returns uploader
*/
setUploaderOptions: function (uploader) {
return uploader
},
/**
* 触发uploadSuccess事件时执行
* 当文件上传成功时触发
* @param {Object} res 上传接口返回的response
* @returns {Boolean} 上传接口返回的response成功状态条件 (比如: res.code == 200)
*/
getResponseSuccess: function (res) {
// console.log(context)
return true
},
/* 指定上传接口返回的response中图片路径的字段,默认为 url */
imageSrcField: 'url'
}
}
4、plupload图片上传
plupload
我们公司图片上传是需要上传至阿里云oss的,这里用的较多的是plupload上传插件。
plupload的特性
- 自适应上传方式。根据程序当前的环境会在HTML5、flash、silverlight、input这些方式中选择最优方案 ,如果支持Html5的话他会优先使用这种方式。
- 压缩图片可以在前端进行处理
- 大文件分片上传,分割成小片进行一个大文件的上传
plupload安装
yarn add plupload // 或者 npm install plupload --save
实例化对象
// 实例化一个plupload上传对象
let uploader = new plupload.Uploader({
multi_selection: false, // 禁止多文件上传
browse_button: id, // 触发文件选择对话框的按钮,为那个元素id
url: 'http://oss-cn-hangzhou.aliyuncs.com', // 服务器端的上传页面地址
filters: {
mime_types: [ // 只允许上传图片
{title: 'Image files',extensions: 'jpg,png,gif,jpeg'}
],
max_file_size: '5mb', // 最大只能上传5mb的文件
prevent_duplicates: false // 不允许选取重复文件
},
resize: {
width: 320, // 压缩后宽
height: 240, // 压缩后高
crop: true, // 开启图片裁剪
quality: 55, // 裁剪质量
preserve_headers: false // 压缩后是否保留图片的元数据
},
init: {
FilesAdded (up, files) {
// 添加文件后的业务逻辑
},
FileUploaded: function (up, file, res) {
// 上传完成后的业务逻辑
},
Error: function (up, err) {
// 错误信息处理
}
}
})
图片校验
上传文件,一般都会对图片进行校验,例如文件的类型、大小、格式、尺寸等。图片校验的配置在实例化传参,如果校验发现不符会调用Error。
初始化
uploader.init() // 初始化
上传结果
getImgUrl (host, fileName) {
let url = host + '/' + fileName
this.$emit('getUrl', {
url
})
}
单个上传组件
封装成单个组件,只需要在组件内完成上传逻辑,并绑定触发上传逻辑的dom元素即可。在上传成功后向父组件$emit通信即可
<template>
<div class="upload">
<button class="upload-btn primary-btn" :id="id" >
<i class="el-icon-plus avatar-uploader-icon"></i>
</button>
</div>
</template>
<script>
export default {
props: {
id: String, // 图片id(每次调用的时候都是需要唯一的)
size: { // 图片的尺寸
type: String,
default: '2048'
}
},
mounted () {
this.initPlupload(this.id)
}
}
</script>
5 富文本编辑器集成
这里采用的富文本编辑器是国产小巧的wangeditor,这个编辑器提供了三方上传方式的集成:
- 使用 base64 保存图片
- 上传图片到服务器
- 上传到七牛云存储
但是上传到阿里云oss需要我们自己研究实现。实现原理同上,wangeditor
提供了图片上传按钮的dom元素,即 editor.imgMenuId
,只需要把这个id绑定到之前的plupload即可实现上传
this.initPlupload(this.editor.imgMenuId)
图片上传配置方法要在编辑器配置之前调用
6 base64上传
我们之前提到的上传都是自动的,即plupload初始化提供的dom元素点击后触发上传。那么如果想手动上传文件该怎么实现呢?
在plupload文档中,有addFile()
这个方法,当这个方法成功调用后就会走之前的逻辑,上传也一气呵成。
下面一个示例是使用input标签手动完成上传
<input type="file" value="请选择文件" @change="uploadImg()" ref="uploadBtn"/>
uploadImg () {
let files = this.$refs.uploadBtn.files[0]
this.uploader.addFile(file)
}
在实现了文件手动上传之后,在实现base64图片上传,只需要把base64转换成file即可
uploadBase64 (base64Str) {
let file = this.getFile(base64Str, new Date().getTime() + '.png')
this.uploader.addFile(file)
}
getFile (dataurl, filename) {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}
7 react集成
初始化方式同vue一样
componentDidMount(){
this.initPlupload(this.state.id)
}
// 获取上传结果
getImgUrl (host, fileName) {
let url = host + '/' + fileName
console.log(url)
this.props.callback(url); // 子组件传给父组件
}
8 jQuery集成
集成方法大同小异,原理都差不多。绑定Dom
-> 传入文件
-> 获取token
-> 上传