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