UNPKG

imgfunc

Version:

图片上传JS

261 lines (252 loc) 8 kB
/** * 暂不考虑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;