From d3a618f09ab510de5c4ca772c44f60c218b61964 Mon Sep 17 00:00:00 2001 From: king <18310653075@163.com> Date: 星期六, 18 九月 2021 15:12:30 +0800 Subject: [PATCH] 2021-09-18 --- src/tabviews/zshare/fileupload/index.jsx | 476 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 402 insertions(+), 74 deletions(-) diff --git a/src/tabviews/zshare/fileupload/index.jsx b/src/tabviews/zshare/fileupload/index.jsx index 682498f..3ef6469 100644 --- a/src/tabviews/zshare/fileupload/index.jsx +++ b/src/tabviews/zshare/fileupload/index.jsx @@ -1,136 +1,464 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' -import { Upload, message, Button, Icon, Progress } from 'antd' +import { is, fromJS } from 'immutable' +import moment from 'moment' +import { Upload, Button, Icon, Progress, notification } from 'antd' +import SparkMD5 from 'spark-md5' import Api from '@/api' import './index.scss' -let service = window.GLOB.service ? (/\/$/.test(window.GLOB.service) ? window.GLOB.service : window.GLOB.service + '/') : '' -let Url = '/Upload' +let service = '' if (process.env.NODE_ENV === 'production') { - Url = document.location.origin + '/' + service + 'zh-CN/Home/Upload' + service = document.location.origin + '/' + window.GLOB.service +} else { + service = window.GLOB.location + '/' + window.GLOB.service } class FileUpload extends Component { static propTpyes = { - value: PropTypes.array // 鎸夐挳淇℃伅銆佽〃鍗曞垪琛� + config: PropTypes.object, // 琛ㄥ崟淇℃伅 + onChange: PropTypes.func, // 琛ㄥ崟鍙樺寲 } state = { - baseUrl: Url, percent: 0, - showprogress: false + accept: '', + accepts: null, + maxFile: null, + rduri: '', + limit: 2, + compress: false, + fileType: 'text', + showprogress: false, + filelist: [] } - init = async () => { - try { - const OSSData = await this.mockGetOSSData() + UNSAFE_componentWillMount () { + const { config } = this.props - this.setState({ - OSSData - }) - } catch (error) { - message.error(error) + let filelist = [] + if (config.initval) { + try { + filelist = config.initval.split(',').map((url, index) => { + return { + uid: `${index}`, + name: url.slice(url.lastIndexOf('/') + 1), + status: 'done', + url: url, + origin: true + } + }) + } catch (e) { + filelist = [] + } } + + let accept = '' + let accepts = null + let compress = false + if (config.compress === 'true') { + compress = true + accepts = ['.jpg', '.png', '.gif', '.jpeg'] + accept = accepts.join(',') + } else if (config.suffix) { + accepts = config.suffix.split(',').map(item => { + if (!/^\./ig.test(item)) { + item = '.' + item + } + return item + }) + accept = accepts.join(',') + } + let rduri = config.rduri || '' + + if (window.GLOB.systemType === 'production') { + rduri = config.proRduri || '' + } + + this.setState({ + rduri, + accept, + accepts, + filelist, + compress, + limit: config.limit || 2, + maxFile: config.maxfile && config.maxfile > 0 ? config.maxfile : null, + fileType: config.fileType || 'text' + }) + } + + shouldComponentUpdate (nextProps, nextState) { + return !is(fromJS(this.state), fromJS(nextState)) } onChange = ({ fileList }) => { - const { onChange } = this.props + fileList = fileList.map(item => { + if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) { + item.response = '' + } + return item + }) - if (onChange) { - onChange([...fileList]) - } + this.setState({filelist: fileList}) } onRemove = file => { - const { value, onChange } = this.props + const files = this.state.filelist.filter(v => v.uid !== file.uid) - const files = value.filter(v => v.url !== file.url) + this.setState({filelist: files}) - if (onChange) { - onChange(files) - } + let vals = [] + + files.forEach(item => { + if (item.origin && item.url) { + vals.push(item.url) + } else if (!item.origin && item.status === 'done' && item.response) { + vals.push(item.response) + } + }) + + this.props.onChange(vals.join(',')) } - getExtraData = () => { - return { - RootPath: 'Content/images/upload/' + onUpdate = (url) => { + let filelist = fromJS(this.state.filelist).toJS() + + if (filelist[filelist.length -1]) { + filelist[filelist.length -1].status = 'done' + filelist[filelist.length -1].response = url + filelist[filelist.length -1].origin = false } + + filelist = filelist.filter(item => !!(item.url || item.response)) + + let vals = [] + + filelist.forEach(item => { + if (item.origin && item.url) { + vals.push(item.url) + } else if (!item.origin && item.status === 'done' && item.response) { + vals.push(item.response) + } + }) + + this.setState({filelist}) + this.props.onChange(vals.join(',')) } - shardupload = (file, shardSize, shardCount, i, fileList) => { - let start = i * shardSize - let end = Math.min(file.size, start + shardSize) + onFail = (msg) => { + let filelist = this.state.filelist.map(item => { + if (!item.url && !item.response && !item.status) { + item.status = 'error' + } + return item + }) + + this.setState({filelist, showprogress: false, percent: 0}) + + notification.warning({ + top: 92, + message: msg || '鏂囦欢涓婁紶澶辫触锛�', + duration: 5 + }) + } + + shardupload = (params) => { + let param = params.chunks.shift() let form = new FormData() - form.append('file', file.slice(start, end)) //slice鏂规硶鐢ㄤ簬鍒囧嚭鏂囦欢鐨勪竴閮ㄥ垎 - form.append('RootPath', 'Content/images/upload/') - form.append('name', file.name) - form.append('total', shardCount) - form.append('index', i + 1) + form.append('file', param.binary) + form.append('fileMd5', params.file.fileMd5) + form.append('shardingMd5', param.chunkMd5) + form.append('baseDomain', service) + form.append('rootPath', 'Content/images/upload/') + form.append('fileName', params.file.fileName) + form.append('fileExt', params.file.fileType) + form.append('shardingCnt', param.chunks) + form.append('shardingNo', param.chunk) + form.append('LoginUID', sessionStorage.getItem('LoginUID') || '') + form.append('UserID', sessionStorage.getItem('UserID') || '') - if (i < shardCount) { - i++ - Api.getFileUpload(form).then(res => { - if (res) { + Api.getLargeFileUpload(form).then(res => { + if (res.status) { + if (params.chunks.length > 0) { this.setState({ - percent: Math.floor(100 * (i / shardCount)) + percent: Math.floor(100 * (param.chunk / param.chunks)) }) - this.shardupload(file, shardSize, shardCount, i, fileList) + this.shardupload(params) + } else { + if (res.urlPath) { + this.onUpdate(res.urlPath) + } else { + this.onFail() + } + this.setState({ + percent: 100 + }, () => { + setTimeout(() => { + this.setState({ + showprogress: false, + percent: 0 + }) + }, 200) + }) } - }) - } else { - this.setState({ - percent: 100 - }, () => { - setTimeout(() => { - this.setState({ - showprogress: false, - percent: 0 - }) - }, 200) - }) - } + } else { + this.onFail(res.message) + } + }) } - beforeUpload = (file, fileList) => { - let shardSize = 2 * 1024 * 1024 - // let shardSize = 3 * 1024 + getuuid = () => { + let uuid = [] + let _options = '0123456789abcdefghigklmnopqrstuv' + for (let i = 0; i < 19; i++) { + uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1)) + } + uuid = uuid.join('') + return uuid + } - if (file.size > shardSize) { - this.setState({ - showprogress: true, - percent: 0 + beforeUpload = (file) => { + const { accepts, compress, limit, rduri } = this.state + + if (accepts && file.name) { + let pass = false + accepts.forEach(type => { + if (new RegExp(type + '$', 'ig').test(file.name)) { + pass = true + } }) - let shardCount = Math.ceil(file.size / shardSize) - this.shardupload(file, shardSize, shardCount, 0, fileList) + + if (!pass) { + setTimeout(() => { + this.onFail('鏂囦欢鏍煎紡閿欒锛�') + }, 10) + return false + } + } + + this.setState({ + showprogress: true, + percent: 0 + }) + + if (compress) { + let reader = new FileReader() + let fileSize = file.size / 1024 / 1024 + let compressRate = 0.9 + + if (fileSize / limit > 5) { + compressRate = 0.4 + } else if (fileSize / limit > 4) { + compressRate = 0.5 + } else if (fileSize / limit > 3) { + compressRate = 0.6 + } else if (fileSize / limit > 2) { + compressRate = 0.7 + } else if (fileSize > limit) { + compressRate = 0.8 + } + + reader.onload = (e) => { + let img = new Image() + let maxW = 640 + + img.onload = () => { + let cvs = document.createElement( 'canvas') + let ctx = cvs.getContext( '2d') + + if (img.width > maxW) { + img.height *= maxW / img.width + img.width = maxW + } + + cvs.width = img.width + cvs.height = img.height + + ctx.clearRect(0, 0, cvs.width, cvs.height) + ctx.drawImage(img, 0, 0, img.width, img.height) + + let param = {Base64Img: cvs.toDataURL('image/jpeg', compressRate)} + + if (rduri) { + param.rduri = rduri + } + + Api.fileuploadbase64(param).then(result => { + if (result.status && result.Images) { + let url = service + result.Images + + if (rduri) { + url = rduri.replace(/webapi(.*)$/, '') + result.Images + } + + this.onUpdate(url) + + this.setState({ + percent: 100 + }, () => { + setTimeout(() => { + this.setState({ + showprogress: false, + percent: 0 + }) + }, 200) + }) + } else { + this.onFail(result.message) + } + }) + } + + img.onerror = () => { + this.onFail('鍥剧墖璇诲彇澶辫触锛�') + } + + img.src = e.target.result + } + + reader.onerror = () => { + this.onFail('鏂囦欢璇诲彇澶辫触锛�') + } + + reader.readAsDataURL(file) return false - } else { - return true + } + + // 鍏煎鎬х殑澶勭悊 + let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice + let chunkSize = 1024 * 1024 * 2 // 鍒囩墖姣忔2M + let chunks = Math.ceil(file.size / chunkSize) // 鍒囩墖鎬绘暟 + let currentChunk = 0 // 褰撳墠涓婁紶鐨刢hunk + let spark = new SparkMD5.ArrayBuffer() // 瀵筧rrayBuffer鏁版嵁杩涜md5鍔犲瘑锛屼骇鐢熶竴涓猰d5瀛楃涓� + let chunkFileReader = new FileReader() // 鐢ㄤ簬璁$畻鍑烘瘡涓猚hunkMd5 + let totalFileReader = new FileReader() // 鐢ㄤ簬璁$畻鍑烘�绘枃浠剁殑fileMd5 + let params = {chunks: [], file: {}} // 鐢ㄤ簬涓婁紶鎵�鏈夊垎鐗囩殑md5淇℃伅 + + params.file.fileName = file.name.replace(/\.{1}[^.]*$/ig, '') // 鏂囦欢鍚嶏紙鍘婚櫎鍚庣紑鍚嶏級 + params.file.fileType = file.name.replace(/^.*\.{1}/ig, '') // 鏂囦欢绫诲瀷 + params.file.fileSize = file.size // 鏂囦欢澶у皬 + params.file.fileChunks = chunks // 璁板綍鎵�鏈塩hunks鐨勯暱搴� + + if (!/^[A-Za-z0-9]+$/.test(params.file.fileName)) { // 鏂囦欢鍚嶇О鍚湁鑻辨枃鍙婃暟瀛椾箣澶栧瓧绗︽椂锛屽悕绉扮郴缁熺敓鎴� + params.file.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid() + } + + totalFileReader.readAsArrayBuffer(file) + totalFileReader.onload = (e) => { // 瀵规暣涓猼otalFile鐢熸垚md5 + spark.append(e.target.result) + params.file.fileMd5 = spark.end() // 璁$畻鏁翠釜鏂囦欢鐨刦ileMd5 + + let _param = new FormData() + _param.append('fileMd5', params.file.fileMd5) + + Api.getFilePreUpload(_param).then(res => { + if (res.status && res.urlPath) { + this.onUpdate(res.urlPath) + this.setState({ + percent: 100 + }, () => { + setTimeout(() => { + this.setState({ + showprogress: false, + percent: 0 + }) + }, 200) + }) + } else if (res.shardings && res.shardings.length > 0) { + res.shardings.forEach(shard => { + if (shard.shardingNo && parseInt(shard.shardingNo) > currentChunk) { + currentChunk = parseInt(shard.shardingNo) + } + }) + loadNext() + } else { + loadNext() + } + }) + } + + chunkFileReader.onload = (e) => { + spark.append(e.target.result) // 瀵规瘡涓�鐗囧垎鐗囪繘琛宮d5鍔犲瘑 + + params.chunks[params.chunks.length - 1].chunkMd5 = spark.end() // 娣诲姞鍒囩墖md5 + + currentChunk++ // 姣忎竴娆″垎鐗噊nload,currentChunk閮介渶瑕佸鍔狅紝浠ヤ究鏉ヨ绠楀垎鐗囩殑娆℃暟 + + if (currentChunk < chunks) { // 褰撳墠鍒囩墖鎬绘暟娌℃湁杈惧埌鎬绘暟鏃� + loadNext() + } else { + this.shardupload(params) + } + } + + chunkFileReader.onerror = () => { + this.onFail('鏂囦欢璇诲彇澶辫触锛�') + } + totalFileReader.onerror = () => { + this.onFail('鏂囦欢璇诲彇澶辫触锛�') + } + + let loadNext = () => { + let start = currentChunk * chunkSize // 璁$畻鍒嗙墖鐨勮捣濮嬩綅缃� + let end = Math.min(file.size, start + chunkSize) // 璁$畻鍒嗙墖鐨勭粨鏉熶綅缃� + + let obj = { // 姣忎竴涓垎鐗囬渶瑕佸寘鍚殑淇℃伅 + chunk: currentChunk + 1, + binary: file.slice(start, end), + start: start, + end: end, + chunks + } + + params.chunks.push(obj) + chunkFileReader.readAsArrayBuffer(blobSlice.call(file, start, end)) + } + + return false + } + + /** + * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊 + */ + componentWillUnmount () { + this.setState = () => { + return } } render() { - const { value } = this.props - const { showprogress, percent, baseUrl } = this.state + const { showprogress, percent, filelist, maxFile, fileType, accept } = this.state + + let uploadable = 'fileupload-form-container ' + + if (maxFile && filelist.length >= maxFile) { + uploadable += 'limit-fileupload' + } const props = { name: 'file', disabled: showprogress, - fileList: value, - action: baseUrl, + listType: fileType, + fileList: filelist, + action: null, + accept: accept, method: 'post', - multiple: true, - // headers: {'RootPath': 'Content/images/upload/'}, + multiple: false, onChange: this.onChange, onRemove: this.onRemove, - data: this.getExtraData, beforeUpload: this.beforeUpload, + className: uploadable } + return ( <Upload {...props}> - <Button> + {fileType !== 'picture-card' ? <Button> <Icon type="upload" /> 鐐瑰嚮涓婁紶 - </Button> + </Button> : null} + {fileType === 'picture-card' ? <span style={{whiteSpace: 'nowrap'}}> + <Icon type="upload" /> 鐐瑰嚮涓婁紶 + </span> : null} {showprogress ? <Progress percent={percent} size="small" /> : null} </Upload> ) -- Gitblit v1.8.0