import React, {Component} from 'react'
|
import PropTypes from 'prop-types'
|
import { is, fromJS } from 'immutable'
|
import moment from 'moment'
|
import { Upload, Button, Progress, notification } from 'antd'
|
import { UploadOutlined } from '@ant-design/icons'
|
import SparkMD5 from 'spark-md5'
|
|
import Api from '@/api'
|
import MKEmitter from '@/utils/events.js'
|
import './index.scss'
|
|
class FileUpload extends Component {
|
static propTpyes = {
|
data: PropTypes.any,
|
config: PropTypes.object,
|
onChange: PropTypes.func,
|
}
|
|
state = {
|
percent: 0,
|
accept: '',
|
accepts: null,
|
maxFile: null,
|
rduri: '',
|
limit: 2,
|
compress: 'false',
|
fileType: 'text',
|
showprogress: false,
|
maxSize: 0,
|
filelist: []
|
}
|
|
UNSAFE_componentWillMount () {
|
const { config, data } = this.props
|
|
let filelist = []
|
if (config.initval) {
|
if (/^data:image/.test(config.initval)) {
|
filelist = [{
|
uid: '0',
|
name: 'data:image/jpeg;base64',
|
status: 'done',
|
url: config.initval,
|
origin: true
|
}]
|
} else {
|
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 = []
|
}
|
|
if (data && config.subFields && config.subFields.length === 1 && filelist.length === 1) {
|
if (data[config.subFields[0].field]) {
|
filelist[0].name = data[config.subFields[0].field]
|
}
|
}
|
}
|
}
|
|
let accept = ''
|
let accepts = null
|
let compress = config.compress || 'false'
|
let maxFile = config.maxfile && config.maxfile > 0 ? config.maxfile : null
|
if (compress === 'true' || compress === 'base64') {
|
accepts = ['.jpg', '.png', '.gif', '.jpeg']
|
accept = accepts.join(',')
|
if (compress === 'base64') {
|
maxFile = 1
|
}
|
} 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,
|
maxSize: config.maxSize || 0,
|
maxFile: maxFile,
|
fileType: config.fileType || 'text'
|
})
|
}
|
|
shouldComponentUpdate (nextProps, nextState) {
|
return !is(fromJS(this.state), fromJS(nextState))
|
}
|
|
onChange = ({ fileList }) => {
|
fileList = fileList.map(item => {
|
if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) {
|
item.response = ''
|
}
|
return item
|
})
|
|
this.setState({filelist: fileList})
|
}
|
|
onRemove = file => {
|
const files = this.state.filelist.filter(v => v.uid !== file.uid)
|
|
this.setState({filelist: 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(','))
|
}
|
|
onUpdate = (url, file_name) => {
|
const { config } = this.props
|
|
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})
|
|
if (config.subFields && file_name) {
|
let other = {}
|
config.subFields.forEach((n, i) => {
|
other[n.field] = file_name
|
setTimeout(() => {
|
MKEmitter.emit('mkFC', 'input', n.uuid, file_name)
|
}, i * 5)
|
})
|
|
this.props.onChange(vals.join(','), other)
|
} else {
|
this.props.onChange(vals.join(','))
|
}
|
}
|
|
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 = (param, file_name) => {
|
let form = new FormData()
|
|
form.append('file', param.binary)
|
form.append('fileMd5', param.fileMd5)
|
form.append('shardingMd5', param.fileMd5)
|
form.append('baseDomain', window.GLOB.baseurl)
|
form.append('rootPath', 'Content/images/upload/')
|
form.append('fileName', param.fileName)
|
form.append('fileExt', param.fileType)
|
form.append('shardingCnt', 1)
|
form.append('shardingNo', 1)
|
form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
|
form.append('UserID', sessionStorage.getItem('UserID') || '')
|
|
Api.getLargeFileUpload(form).then(res => {
|
if (res.status) {
|
if (res.urlPath) {
|
this.onUpdate(res.urlPath, file_name)
|
} else {
|
this.onFail()
|
}
|
this.setState({
|
percent: 100
|
}, () => {
|
setTimeout(() => {
|
this.setState({
|
showprogress: false,
|
percent: 0
|
})
|
}, 200)
|
})
|
} else {
|
this.onFail(res.message)
|
}
|
})
|
}
|
|
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
|
}
|
|
beforeUpload = (file) => {
|
const { accepts, compress, limit, rduri, maxSize } = this.state
|
|
if (accepts && file.name) {
|
let pass = false
|
accepts.forEach(type => {
|
if (new RegExp(type + '$', 'ig').test(file.name)) {
|
pass = true
|
}
|
})
|
|
if (!pass) {
|
setTimeout(() => {
|
this.onFail('文件格式错误!')
|
}, 10)
|
return false
|
}
|
}
|
if (maxSize) {
|
let fileSize = file.size / 1024 / 1024
|
|
if (fileSize > maxSize) {
|
setTimeout(() => {
|
this.onFail(`文件大小不可超过${maxSize}M`)
|
}, 10)
|
return false
|
}
|
}
|
|
this.setState({
|
showprogress: true,
|
percent: 0
|
})
|
|
let file_name = file.name
|
|
if (compress === 'true' || compress === 'base64') {
|
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 (this.props.config.compress === 'base64') {
|
this.onUpdate(param.Base64Img, file_name)
|
|
this.setState({
|
percent: 100
|
}, () => {
|
setTimeout(() => {
|
this.setState({
|
showprogress: false,
|
percent: 0
|
})
|
}, 200)
|
})
|
} else {
|
if (rduri) {
|
param.rduri = rduri
|
}
|
|
Api.fileuploadbase64(param).then(result => {
|
if (result.status && result.Images) {
|
let url = window.GLOB.baseurl + result.Images
|
|
if (rduri) {
|
url = rduri.replace(/webapi(.*)$/, '') + result.Images
|
}
|
|
this.onUpdate(url, file_name)
|
|
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 if (compress === 'oss') {
|
let _param = new FormData()
|
_param.append('multipartFile', file)
|
_param.append('userId', sessionStorage.getItem('UserID') || '')
|
|
Api.fileOssUpload(_param).then(res => {
|
if (res.status) {
|
if (res.urlPath) {
|
let path = (/^\/\//.test(res.urlPath) ? 'https:' : '') + res.urlPath
|
this.onUpdate(path, file_name)
|
this.setState({
|
percent: 100
|
}, () => {
|
setTimeout(() => {
|
this.setState({
|
showprogress: false,
|
percent: 0
|
})
|
}, 200)
|
})
|
} else {
|
this.onFail('文件上传失败!')
|
}
|
} else {
|
this.onFail(res.message || '文件上传失败!')
|
}
|
})
|
|
return false
|
}
|
|
// 兼容性的处理
|
let spark = new SparkMD5.ArrayBuffer() // 对arrayBuffer数据进行md5加密,产生一个md5字符串
|
let totalFileReader = new FileReader() // 用于计算出总文件的fileMd5
|
let param = {}
|
|
param.fileName = file.name.replace(/\.{1}[^.]*$/ig, '') // 文件名(去除后缀名)
|
param.fileType = file.name.replace(/^.*\.{1}/ig, '') // 文件类型
|
|
if (!/^[A-Za-z0-9]+$/.test(param.fileName)) { // 文件名称含有英文及数字之外字符时,名称系统生成
|
param.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
|
}
|
|
totalFileReader.readAsArrayBuffer(file)
|
totalFileReader.onload = (e) => { // 对整个totalFile生成md5
|
spark.append(e.target.result)
|
param.fileMd5 = spark.end() // 计算整个文件的fileMd5
|
param.binary = file
|
|
let _param = new FormData()
|
_param.append('fileMd5', param.fileMd5)
|
|
Api.getFilePreUpload(_param).then(res => {
|
if (res.status && res.urlPath) {
|
this.onUpdate(res.urlPath, file_name)
|
this.setState({
|
percent: 100
|
}, () => {
|
setTimeout(() => {
|
this.setState({
|
showprogress: false,
|
percent: 0
|
})
|
}, 200)
|
})
|
} else {
|
this.shardupload(param, file_name)
|
}
|
})
|
}
|
|
totalFileReader.onerror = () => {
|
this.onFail('文件读取失败!')
|
}
|
|
return false
|
}
|
|
/**
|
* @description 组件销毁,清除state更新
|
*/
|
componentWillUnmount () {
|
this.setState = () => {
|
return
|
}
|
}
|
|
render() {
|
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,
|
listType: fileType,
|
fileList: filelist,
|
action: null,
|
accept: accept,
|
method: 'post',
|
multiple: false,
|
onChange: this.onChange,
|
onRemove: this.onRemove,
|
beforeUpload: this.beforeUpload,
|
className: uploadable
|
}
|
|
return (
|
<Upload {...props}>
|
{fileType !== 'picture-card' ? <Button>
|
<UploadOutlined /> 点击上传
|
</Button> : null}
|
{fileType === 'picture-card' ? <span style={{whiteSpace: 'nowrap'}}>
|
<UploadOutlined /> 点击上传
|
</span> : null}
|
{showprogress ? <Progress percent={percent} size="small" /> : null}
|
</Upload>
|
)
|
}
|
}
|
|
export default FileUpload
|