如何做好一个文件上传
1 DOM-fileUpload
- 在还没有ajax异步请求之前,通常会采用表单的input上传文件,同步上传,最核心的部分就是DOM的
FileUpload对象——input[type="file"]
<form id="upload" method="post" action="/upload_file" enctype="mulipart/form-data">
<input type="file" id="inputFile" name="file"></input>
<input type="submit" value="提交"></input>
</form>
-
显然,这种原生的input文件需要借助jS的submit提交,还需要设置name属性,上传的类型为文件类型,有时候图片需要base64或者blob就需要自行解决了。
-
如果直接上传会刷新页面的
解决方法如下 1. 通过给submit按钮添加 return false 2. 阻止form的默认行为 e.preventDefault() -
重置样式,并不是真正的隐藏
form { display: inline-block; position: relative; overflow: hidden; } input[type="file"] { position: absolute; top: 0; right: 0; _zoom:30; margin: 0; padding: 0; height: 100%; _height:auto; font-size: 3000px \9; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); opacity: 0; cursor: pointer; } -
这样同步上传文件是不利于对文件的处理的,比如JS压缩等操作,最近这段时间,接手一个老项目,确实不好处理,也没有去用压缩库
2AJAX
- 由于老项目需要,就采取
jquery来调用ajax - 可以采用等比压缩和质量压缩,只要依靠
canvas - 实现方法
// 由于采用了jquery,还是要支持复用的,需要传入$(this)进来,就不直接获取dom.getElementById('file').files[0]了
function compressAndUpdate(input, cb) {
const $self = input;
// 获取file类型
let fileObj = $self[0].files[0]
// 可以直接用fileobj获取fileSize等等
let file = $self.val().split('.')
let filetype = file[file.length-1].toLowerCase()
let fileSize = 0;
let browser = navigator.appName; // 浏览器类型
if(!file[0]) {
return;
}
// 非ie浏览器
if (browser !== 'Microsoft Internet Explorer') {
filesize = $self.get(0).files[0].size;
}
if (filesize >= 10 * 1024 * 1024) {
$self.val('');
toast.showError('上传文件大小不能超过10M!');
return false;
}
if (filetype !== 'jpg'&& filetype !== 'jpeg' && filetype !== 'gif' && filetype !== 'png' && filetype !== 'bmp') {
$self.val('');
toast.showError('上传文件格式不符合要求!');
return false;
}
// 一大波判断结束后,可以开始我们的压缩了
let reader = new FileReader()
reader.readAsDataURL(fileObj)
reader.onload = function(e) {
let image = new Image()
image.src = e.target.result;
image.onload = function() {
// 创建canvas压缩,可以判断多大才去压缩,或者宽高多大,压缩多少
let canvas = document.createElement('canvas')
let context = canvas.getContext('2d')
let imageWidth = image.width / 2 // 压缩后图片的大小
let imageHeight= image.height / 2
let data = ''
// 将图片大小赋值在canvas大小上
canvas.width = imageWidth;
canvas.height = imageHeight;
context.drawImage(image, 0,0, imageWidth, imageHeight)
data = canvas.toDataURL('image/jpeg', 0.5) // 输出64, 0.5是量
var obj = dataURLtoFile(data, fileObj.name) // 64转成file类型
var form1 = new FormData();
form1.append('avatar', obj)
$.ajax({
url: 'xxxxxxxxxxxxxxxxxxxx',
type: 'POST',
cache: false,
processData: false,
contentType: false,
data: form1,
success: function (response) {
// 成功
}
})
}
}
if (typeof cb === 'function') {
cb();
}
}
function dataURLtoFile(dataurl, filename) {//将base64转换为文件
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}
3 图片格式转化
1 图片常见的格式
-
file,blob,base64 -
参考 https://www.cnblogs.com/whitewen/articles/10456410.html#3
4 预览(主要针对图片预览)
普通青年的图片预览方式是待文件上传成功后,后台返回上传文件的url,然后把预览图片的img元素的src指向该url。这其实达不到预览的效果和目的。
属于文艺青年的现代浏览器又登场了:“使用HTML5的FileReader API吧!” 让我们直接上代码,直奔主题:
方法1: reader.onload回调的result指向image的src
function handleImageFile(file) {
var previewArea = document.getElementById('previewArea');
var img = document.createElement('img');
var fileInput = document.getElementById("myFile");
var file = fileInput.files[0];
img.file = file;
previewArea.appendChild(img);
var reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
// 此处的e是reader, 也可以this, 将result返回给src
aImg.src = e.target.result;
}
})(img);
reader.omprogress = function(e) {
// e.loaded/e.total
}
reader.readAsDataURL(file);
}
FileReader实例有以下方法:
- abort : 中断读取操作;
- readAsArrayBuffer : 读取文件内容到ArrayBuffer对象中;
- readAsBinaryString : 将文件读取为二进制数据;
- readAsDataURL : 将文件读取为data : URL格式的字符串;
- readAsText : 将文件读取为文本;
- onprogress : 文件读取进度;
- onload : 文件读取加载;
使用FileReader来处理图片的异步加载,在创建新的FileReader对象之后,我们建立了onload函数,然后调用readAsDataURL()开始在后台进行读取操作。当图像文件加载后,转换成一个 data: URL 保存在result属性上,并传递到onload回调函数中设置给img的src。
方法2: 无需FileReader, 直接将file转换URL
file本来就是就继承自Blob对象,可以URL.createObjectURL()函数创建URl
var img = document.createElement("img");
// 全局函数URL生成一个网络地址————createObjectURL, revokeObjectURL
img.src = window.URL.createObjectURL(file);
img.onload = function() {
// 明确地通过调用释放
window.URL.revokeObjectURL(this.src);
}
previewArea.appendChild(img);
5 拖拽和裁剪的支持
- 拖拽上传和图片裁剪处理功能,这是高科技功能,可以帮你上传组件增加不少亮点; 其实主要是利用HTML5的drag & drop事件,我们可以很快实现对拖拽的支持。首先我们可能需要确定一个允许拖拽的区域,然后绑定相应的事件进行处理。
var dropArea;
dropArea = document.getElementById("dropArea");
dropArea.addEventListener("dragenter", handleDragenter, false);
dropArea.addEventListener("dragover", handleDragover, false);
dropArea.addEventListener("drop", handleDrop, false);
// 阻止dragenter和dragover的默认行为,这样才能使drop事件被触发
function handleDragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
// handle files ...
}
- 同样可以用到原生JS,
mousedownmouseup
// 直接采用vue的
directives: {
drap: {
bind: function(el) {
const oDiv = el;
// 获取绑定元素的宽高
oDiv.style.position = 'absolute';
const width = oDiv.style.width;
const height = oDiv.style.width;
const minWidth = 100,
minHeight = 100;
oDiv.onmousedown = (e) => {
// 获取鼠标相对位置
e.stopPropagation();
const pWidth = e.target.parentNode.getClientRects()[0].width;
const pHeight = e.target.parentNode.getClientRects()[0].height;
// console.log(e.target.parentNode.getClientRects()[0].width);
const l = e.clientX - oDiv.offsetLeft;
const t = e.clientY - oDiv.offsetTop;
let left = 0,
top = 0;
document.onmousemove = (e) => {
let left = e.clientX - l;
let top = e.clientY - t;
oDiv.style.left = `${left}px`;
oDiv.style.top = `${top}px`;
};
document.onmouseup = (e) => {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
}
这里可以把通过事件对象的dataTransfer拿到的files数组和之前相同处理,以实现预览上传等功能。有了这些事件回调,我们也可以在不同的事件给我们UI元素添加不同的class来实现更好交互效果。
裁剪
mousedown
mouseup
mousemove
canvas
未完待续...