imgfunc
Version:
图片上传JS
261 lines (252 loc) • 8 kB
JavaScript
/**
* 暂不考虑gif
*/
import EXIF from "@nuofe/exif-js";
import { FILETYPE, IMAGEERROR } from "./errorType";
import {STATIC_TIMEOUT, STATIC_RESPONSE_ERROR, STATIC_FILE_ERROR, STATIC_COMPRESS_ERROR, STATIC_EXCHANGE_ERROR} from './statusEnum'
import createImage from "./image";
import Compress from "./compress";
import XHRUpload from "./upload";
const defaultSize = 2 * 1024 * 1024; // 默认大小2M
/**
* 给Function 类型的变量赋默认值
* @param {Function} func
* @param {*} type
*/
function defaultValue(func, type) {
return func || (() => undefined);
}
class Upload {
constructor({
fileType = /\/(?:jpeg|png|gif|jpg)/i,
changeType = "image/jpeg",
elem = null,
base64 = null,
maxSize = defaultSize,
multiple = false,
beforeUpload,
uploading,
successUpload,
errorUpload,
xhrParam,
uploadUrl,
uploadTimeout = 60,
formdataName='file',
fileName='blob'
}) {
this.elem = elem;
this.maxSize = maxSize;
this.base64 = base64;
this.beforeUpload = beforeUpload;
this.uploading = defaultValue(uploading);
this.successUpload = defaultValue(successUpload);
this.errorUpload = defaultValue(errorUpload);
this.errorUpload = defaultValue(errorUpload);
this.fileType = fileType;
this.changeType = changeType;
this.uploadUrl = uploadUrl;
this.uploadTimeout = uploadTimeout;
this.fileName = fileName;
// 上传文件的 name
this.formdataName = formdataName;
// 上传的额外参数
this.xhrParam = xhrParam;
// 如果传入的是节点,需要调用init拿到将图片文件转为base64
if (elem) {
const fileList = elem.files;
if (fileList && fileList.length) {
const files = Array.prototype.slice.call(fileList);
// TODO: 暂时支持单文件上传
if (!multiple) {
const file = files[0];
if (
this.beforeUpload &&
this.beforeUpload({ file, type: "file", status: "getFile" }) ===
false
) {
// 用户传递了beforeUpload 如果返回false 直接中断
return;
} else {
if (!fileType.test(file.type)) {
this.errorUpload({
message: '文件类型错误',
errorType: FILETYPE,
status: STATIC_FILE_ERROR
});
return false;
}
}
this.init(file);
}
} else {
this.errorUpload({
message: '未获取到上传文件',
errorType: FILETYPE,
status: STATIC_FILE_ERROR
});
}
} else if (this.base64) {
// base64 图片无需获取方向信息,可直接执行压缩上传
createImage(this.base64)
.then(image => {
// 将图片的类型统一转换 指定类型 默认 JPEG,
this.changeImage(image);
})
.catch(error => {
this.errorUpload({
message: "创建base64文件失败",
errorType: IMAGEERROR,
status: STATIC_EXCHANGE_ERROR
});
});
}
}
init(imgFile) {
const _that = this;
// 获取照片方向角属性,用户旋转控制
EXIF.getData(imgFile, function() {
const Orientation = EXIF.getTag(this, "Orientation");
_that.Orientation = Orientation;
_that.getFileResult(imgFile);
});
}
getFileResult(imgFile) {
const reader = new FileReader();
reader.readAsDataURL(imgFile);
reader.onload = eventFile => {
const { result } = eventFile.target;
createImage(result)
.then(image => {
// 将图片的类型统一转换 指定类型 默认 JPEG,
this.changeImage(image);
})
.catch(error => {
this.errorUpload({
message: "blob转换错误",
errorType: IMAGEERROR,
status: STATIC_EXCHANGE_ERROR
});
});
};
}
changeImage(image) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const { width, height } = image;
canvas.width = width;
canvas.height = height;
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0, width, height);
if (this.Orientation) {
// 如果方向角不为1,都需要进行旋转
switch (this.Orientation) {
case 6: // 需要顺时针(向左)90度旋转
canvas.width = height;
canvas.height = width;
ctx.rotate((1 * 90 * Math.PI) / 180);
ctx.drawImage(image, 0, -height, width, height);
break;
case 8: // 需要逆时针(向右)90度旋转
canvas.width = height;
canvas.height = width;
ctx.rotate((3 * 90 * Math.PI) / 180);
ctx.drawImage(image, -width, 0, width, height);
break;
case 3: // 需要180度旋转
ctx.rotate((2 * 90 * Math.PI) / 180);
ctx.drawImage(image, -width, -height, width, height);
break;
default:
ctx.drawImage(image, 0, 0, width, height);
break;
}
}
const nbase64 = canvas.toDataURL(this.changeType, 1);
canvas.width = 0;
canvas.height = 0;
// 执行压缩
Compress({
base64: nbase64,
maxSize: this.maxSize,
fileType: this.changeType
})
.then(data => {
// 压缩成功后对上传最后拦截
if (data.result) {
const { base64 } = data;
if (
this.beforeUpload &&
this.beforeUpload({
file: base64,
type: "compress",
status: 200
}) === false
) {
return;
}
this.startUpload(base64);
}
})
.catch(error => {
console.warn(error);
this.errorUpload({
message: "压缩失败",
errorType: IMAGEERROR,
status: STATIC_COMPRESS_ERROR
});
});
}
startUpload(base64) {
const {
changeType,
beforeUpload,
uploading,
successUpload,
errorUpload,
xhrParam,
uploadUrl,
uploadTimeout,
formdataName,
fileName,
} = this;
XHRUpload({
base64,
changeType,
beforeUpload,
uploading,
successUpload,
errorUpload,
xhrParam,
uploadUrl,
uploadTimeout,
formdataName,
fileName
});
}
}
/**
* @description 单图片上传集中处理
* @property {String} uploadUrl 上传文件地址,必传
* @property {RegExp} fileType 文件类型正则, default: /\/(?:jpeg|png|gif|jpg)/
* @property {Object} elem example:{files:[file]} 文件域节点 target
* @property {String} base64 图片base64
* @property {Number} maxSize 文件size最大限制, default:2 * 1024 * 1024; // 默认大小2M
* @property {Boolean} multiple 是否多文件 default:false, PS: 不支持多文件
* @property {Object} xhrParam 上传额外参数
* @property {String} fileName 上传文件名称 用于new FormDate() append方法第三个参数, default:'blob'
* @property {String} formdataName 上传文件 name, default:'file'
* @property {Number} uploadTimeout 执行上传接口时间 单位:s, default:60
* @property {Function} beforeUpload 上传前调用函数 返回 false 阻止上传
* @property {Function} uploading 调用上传接口时触发
* @property {Function} successUpload 调用上传接口成功调用,直接返回接口响应报文JSON格式
* @property {Function} errorUpload 上传失败回调 {status,errorType, message}
* @return ========= 失败详解 =====
* @type status 502 接口上传超时, 999 响应失败, 1000 上传文件异常, 1001 图片压缩异常, 1002 图片转换异常
* @type errorType
* @typw message
*/
function YbsUpload(...args) {
return new Upload(...args);
}
export default YbsUpload;