index.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* eslint-disable prefer-template,no-param-reassign */
  2. import React, { Component } from 'react';
  3. import { Upload, Icon, message, Modal } from 'antd';
  4. import { queryOssSignature } from '../../services/resource';
  5. import { Hotax } from '../../utils/config';
  6. import { setSignature } from '../../utils/signature';
  7. import styles from './index.less';
  8. const Message = message;
  9. // 允许上传的文件类型
  10. const renderAccept = (accept) => {
  11. if (!accept) {
  12. return null;
  13. }
  14. if (['image', 'video', 'audio'].find(ext => ext === accept)) {
  15. return `${accept}/*`;
  16. }
  17. if (accept === 'zip') {
  18. return 'application/zip,application/x-zip,application/x-zip-compressed';
  19. }
  20. return `.${accept}`;
  21. };
  22. // 检查文件大小
  23. const checkFileSize = (fileName, fileSize) => {
  24. if (fileSize > Hotax.FILE_MAX_SIZE * 1000 * 1000) {
  25. Message.error(`${fileName}文件大小超过${Hotax.FILE_MAX_SIZE}M!`);
  26. return false;
  27. }
  28. return true;
  29. };
  30. /**
  31. * 使用时间戳生成随机的文件名
  32. * @param fileName
  33. * @param fileCode
  34. * @returns {string}
  35. */
  36. const getRandomFileName = (fileName, fileCode) => {
  37. const relativePath = fileCode ? fileCode.replace(/-/g, '/') + '/' : '';
  38. const separatorIndex = fileName.lastIndexOf('.');
  39. const suffix = fileName.substring(separatorIndex + 1, fileName.length);
  40. const randomFileName = (new Date()).getTime().toString() + Math.floor(Math.random() * 10000) + '.' + suffix;
  41. return `${relativePath}${randomFileName}`;
  42. };
  43. /**
  44. * 将传入文件对象处理成upload组件格式
  45. * @return {Array} [{}]
  46. */
  47. const renderFileList = (fileList) => {
  48. return fileList.map((file, index) => {
  49. if (file.status === 'uploading') {
  50. return file;
  51. }
  52. return {
  53. url: file.url,
  54. path: file.path,
  55. thumbUrl: file.url,
  56. name: file.name,
  57. size: file.size,
  58. type: file.type,
  59. status: 'done',
  60. uid: index,
  61. processed: true, // 标记是否图片已处理
  62. };
  63. });
  64. };
  65. class Uploader extends Component {
  66. constructor(props) {
  67. super(props);
  68. this.state = {
  69. ossSign: {}, // 保存oss签名
  70. fileList: renderFileList(this.props.fileList), // 文件对象列表
  71. curFileName: '',
  72. previewImg: '',
  73. previewVisible: false,
  74. };
  75. }
  76. handleBeforeUpload = (file) => {
  77. const { fileCode } = this.props;
  78. if (!checkFileSize(file.name, file.size)) {
  79. return false;
  80. }
  81. return queryOssSignature({ fileName: file.name })
  82. .then((res) => {
  83. this.setState({
  84. ossSign: res.data || res,
  85. curFileName: getRandomFileName(file.name, fileCode),
  86. });
  87. if (res.data) {
  88. setSignature(res.data); // 保存签名到本地
  89. }
  90. });
  91. };
  92. handleOnChange = ({ fileList }) => {
  93. const { ossSign, curFileName } = this.state;
  94. const { onUpload, totalLimit } = this.props;
  95. const { dir, host } = ossSign;
  96. const newFileList = fileList.filter((file) => {
  97. if (file.status === 'error') {
  98. Message.error(`${file.name}上传失败!`); // 上传失败给出提示并去掉
  99. return false;
  100. }
  101. if (file.status === 'done' && !file.processed) {
  102. Message.success('上传成功');
  103. file.url = `${host}/${dir}${curFileName}`;
  104. file.path = `${dir}${curFileName}`;
  105. file.thumbUrl = file.url;
  106. file.processed = true;
  107. }
  108. return true;
  109. });
  110. this.setState({
  111. fileList: renderFileList(newFileList),
  112. });
  113. const uploaded = newFileList.filter((file) => {
  114. return file.status === 'done' && file.processed;
  115. });
  116. if (!uploaded.length) { return; }
  117. if (this.props.onChange) {
  118. this.props.onChange(uploaded.splice(0, totalLimit));
  119. } else {
  120. onUpload(uploaded.slice(0, totalLimit));
  121. }
  122. };
  123. handleOnRemove = (file) => {
  124. const newFileList = this.state.fileList.filter((item) => {
  125. return item.uid !== file.uid;
  126. });
  127. if (this.props.onChange) {
  128. this.props.onChange(newFileList);
  129. } else {
  130. this.props.onRemove(newFileList);
  131. }
  132. };
  133. handleOnPreview = (file) => {
  134. this.setState({
  135. previewImg: file.url || file.thumbUrl,
  136. previewVisible: true,
  137. });
  138. };
  139. handleCancelPreview = () => {
  140. this.setState({
  141. previewVisible: false,
  142. });
  143. };
  144. render() {
  145. const { multiple, accept, totalLimit, forbidden } = this.props;
  146. const { ossSign, fileList, curFileName, previewImg, previewVisible } = this.state;
  147. const { host, policy, accessid, signature, dir } = ossSign;
  148. const uploadProps = {
  149. action: host,
  150. headers: {
  151. Authorization: `OSS${accessid}:${signature}`,
  152. },
  153. data: {
  154. policy,
  155. signature,
  156. OSSAccessKeyId: accessid,
  157. key: `${dir}${curFileName}`,
  158. success_action_status: '200',
  159. },
  160. fileList,
  161. multiple,
  162. disabled: !!(forbidden || this.state.fileList.length >= totalLimit),
  163. accept: renderAccept(accept),
  164. listType: 'picture',
  165. beforeUpload: this.handleBeforeUpload,
  166. onPreview: this.handleOnPreview,
  167. onChange: this.handleOnChange,
  168. onRemove: this.handleOnRemove,
  169. };
  170. return (
  171. <div>
  172. <Upload.Dragger {...uploadProps}>
  173. <p className={styles.dragIcon}>
  174. <Icon type="inbox" />
  175. </p>
  176. <p className={styles.dragText}>点击或者拖拽图片到此区域进行上传</p>
  177. <p className={styles.dragHint}>{`支持jpg/png/svg等图片格式,大小需小于${Hotax.FILE_MAX_SIZE}M`}</p>
  178. </Upload.Dragger>
  179. <Modal
  180. visible={previewVisible}
  181. footer={null}
  182. onCancel={this.handleCancelPreview}
  183. width={1100}
  184. >
  185. <img src={previewImg} alt="" style={{ width: '100%', marginTop: 20 }} />
  186. </Modal>
  187. </div>
  188. );
  189. }
  190. }
  191. export default Uploader;