前言
图片上传是常见的需求之一,本文讲解自己的解决过程,以及遇到坑。
调用摄像头
<input type="file" accept="image/*" capture name="" value="">
微信内置浏览器,和一些主流浏览器支持调用摄像头,但也有很多不支持调用摄像头,仅支持相册。
如果是WebView中,就需要客户端支持了,android和ios的权限也是问题。
formData方式
formData简介
简单的说就是:通过formData,我们可以用ajax方式来发送表单数据;以前上传图片是需要用form表单提交的。
界面部分
我们知道浏览器默认显示的文件上传按钮是很丑的,通常UI都会对上传按钮进行设计。有以下几种方案来写样式。
- 方案一
外部写设计样式,内部写上传按钮,并设置上传按钮的透明度。
<div class="upload-wrapper">
upload
<input type="file" name="upload" multiple="multiple" accept="image/*" value="">
</div>
.upload-wrapper {
position: relative;
box-sizing: border-box;
overflow: hidden;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
border-radius: 5px;
border: 1px solid pink;
background: #fff;
}
.upload-wrapper:hover {
background: pink;
color: #fff;
}
input[type="file"] {
box-sizing: border-box;
opacity: 0;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
![](https://img.haomeiwen.com/i1567373/b5a5069da0357cd0.png)
弊端:
- 样式复杂,涉及定位
- cursor设置pointer无效
- 为保证点击上传按钮时,文件窗口弹出,这里是通过事件冒泡起作用的。
- 真正的
input type="file"
由两部分组成,左边的按钮,右边的提示部分;实际上当鼠标在左边的按钮上时,cursor不起作用
- 总会提示title "未选择任何文件"
![](https://img.haomeiwen.com/i1567373/e938a1dfbb06aa58.png)
- 方案二
将input type="file"
隐藏,当点击外部容器时,模拟点击上传按钮。
坑
- 在pc端模拟点击暂时没有发现异常
- 在移动端模拟点击会出现两个问题
- 事件冒泡
点击外部容器 --> 触发模拟点击上传按钮 --> 触发事件冒泡 --> 点击外部容器,导致两个事件递归了。 - 触发无效
当阻止冒泡事件后,确实触发了点击,log不会打印出来,文件窗口不会弹出来;此时切换到pc,发现log打印出来,并且之前的log也打印出来了,文件窗口弹出。
- 事件冒泡
<div class="upload-wrapper" @click="upload('logo_path')">
upload
<input ref="logo_path" type="file" name="logo_path" multiple="multiple" accept="image/*" value="" @change="selectImg('logo_path')">
</div>
.upload-wrapper {
box-sizing: border-box;
overflow: hidden;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
border-radius: 5px;
border: 1px solid pink;
background: #fff;
cursor: pointer;
}
.upload-wrapper:hover {
background: pink;
color: #fff;
}
/* pc端 */
input[type="file"] {
display: none;
}
/* 移动端 */
input[type="file"] {
width: 0;
height: 0;
z-index: -1;
}
通过ref获取上传按钮。
/**
* 触发上传
*/
upload: function (name) {
this.$refs[name].click() // 触发了selectImg函数
},
上传过程
selectImg: function (name) {
// 获取文件
...
// 校验
...
// formData
...
}
获取文件
ref方式
let file = this.$refs[name].files[0]
event.target方式
/**
* 获取图片
*/
function getImg (files) {
var file;
if (files && files.length > 0) {
file = files[0];
}
return file;
}
校验
function verifyImg(file) {
// 空
if (!file) {
return false
}
// 图片格式
if (!/\/(?:jpeg|png|jpg|bmp)/i.test(file.type)) {
// 提示操作
return false
}
// 图片大小
if (file.size > 5 * 1024 * 1024) {
// 提示操作
return false
}
}
formData
坑:
- formData.append(key, value)
key的设置要和后端沟通好。 - 使用axios的时候,不需要设置contentType
我设置multipart/form-data,反而不行,谨记!!!
let formData = new FormData()
formData.append('UploadForm[image]', file)
let that = this
axios.post(url, formData)
.then(function (res) {
// success
})
.catch(function (error) {
console.log(error)
})
图片预览
FileReader
FileReader简介
通过readAsDataURL()
,在读取操作完成后,result属性中将包含一个data:URL格式的字符串以表示所读取文件的内容。
if (window.FileReader) {
let fr = new FileReader()
let that = this
fr.readAsDataURL(file)
fr.onloadend = function (e) {
that.imgUrl = e.target.result
}
}
base64字符串
/**
* 将图片转化成base64字符串,并上传
*/
function base64(file) {
if (window.FileReader) {
var fr = new FileReader();
fr.readAsDataURL(file);
fr.onloadend = function (e) {
var params = e.target.result;
// 以下是去掉data协议
// params = params.replace("data:image/jpeg;base64,", "")
}
}
}
兼容性
我在safari中测试,发现是支持的。
![](https://img.haomeiwen.com/i1567373/1dc4963c14240cd3.png)
URL.createObjectURL()
URL.createObjectURL简介
通过URL.createObjectURL()创建一个URL对象,这个URL对象表示指定的file对象或Blob对象。
this.imgUrl = window.URL.createObjectURL(file)
兼容性
![](https://img.haomeiwen.com/i1567373/5b1d88e4ff9423e6.png)
canvas压缩图片
张鑫旭的文章:HTML5 file API加canvas实现图片前端JS压缩并上传
数据类型
张鑫旭的文章:理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型
网友评论