react+fetch实现批量上传功能

2,081 阅读1分钟

react class类组件+fetch实现批量上传的功能

  • 批量上传在现阶段的工作中一共是有两中实现防范
    • 批量多个上传文件的时候是请求一次接口,然后将所以的formate全部传给后端
    • 是循环拿到批量上传的files,然后前端循环这个files,每一次都需要调用一次后端的接口(本次使用的方式也是这种)
import React, { Component } from 'react';
import { Button, Icon, Toaster } from 'UI';
import URL from './url';
import './index.scss';

export default class UploadBtn extends Component {
  constructor(props) {
    super(...props);
    this.state = {
        data: [],
        loading: false,
    }
  }

  componentDidMount() {
    const { data } = this.props;
    if (!data) {
      this.setState({ data: [] });
    } else {
      this.setState({ data });
    }
  }

  componentWillReceiveProps(props) {
    const { data } = props;
    if (!data) {
      this.setState({ data: [] });
    } else {
      this.setState({ data: data });
    }
  }
  
  // 上传附件格式限制校验
  validateLimitFormat() {

  }

  validateLimitSize() {

  }

  // 上传成功后把数据放到data
  handleImport = (...arg) => {
    const { action, multiple, limit } = arg;
    // console.log(action, multiple, limit)
    const input = document.createElement('input');
    input.type = 'file';
    input.id = 'handle-import';
    input.multiple = true;
    input.click();
    // console.log(input);
    // 大小的限制

    // 格式的限制

    // 批量上传-请求多个接口
    input.addEventListener('change', e => {
      let files = e.target.files;
      // console.log(e.target.files);
      let classFiles = Array.prototype.slice.call(files)
      classFiles.forEach((file, index) => {
        const formData = new FormData();
        formData.append('file', file);
        try {
          this.setState({ loading: true });
          fetch(URL.UPLOAD, {
            method: 'POST',
            body: formData,
            credentials: "same-origin"
          })
            .then(res => {
              return res.json();
            })
            .then(data => {
              // console.log(data)
              if (data.status === 0) {
                Toaster.show({
                  type: 'error',
                  title: (error.data && data.data.message) || '上传失败!',
                });
              } else {
                let imgData = this.state.data;
                imgData.push(data.data);
                this.setState({ 
                  data: imgData, 
                });
              }
              this.setState({ loading: false });
              input.remove();
            }).catch(error => {
              console.log(error)
              Toaster.show({
                type: 'error',
                title: (error.data && error.data.message) || '上传失败!',
                duration: 8
              });
              this.setState({ loading: false });
              throw error;
            });
        } catch (error) {
          console.log(error)
          this.setState({ loading: false });
          throw error;
        } finally {
            
        }
      })
    });
  };

  // 删除
  deleteFile(e, index, fileId) {
    let data = this.state.data;
    data.splice(index, 1);
    this.setState({ data });
  }

  // 暴露出去的钩子
  getValue() {
    return this.state.data || [];
  }

  render () {
    const { data=[], loading } = this.state;
    const { 
      mode="edit",
      type="button",
      disabled = false, 
      shape="", 
      icon="plus", 
      btnType="primary", 
      size="", 
      text="上传", 
      className="",
      action=URL.UPLOAD, 
      multiple=false, 
      limit=[],
      maxLength = 50,
      showBtn,
    } = this.props;
    return (
      <div className={`${className} c-upload-btn file-upload-type-${type}`}>
        <div className={`file-upload-type-${type}`}>
          { (data.length < maxLength) &&
          <Button
            disabled={disabled}
            loading={loading}
            onClick={this.handleImport.bind(this, action, multiple, limit)} 
            shape={shape}
            icon={icon} 
            type={btnType}
            size={size}
          >{text}</Button>
          }
          <div>
            {
              validateArray(data) && data.map((item, index) => 
                <a key={index} className="upload-file-list" href="javascript:void(0)">
                  <span className="upload-file-list-name">
                    <span>{item.fileName}</span>
                  </span>
                  <span className="upload-file-list-close" onClick={e => this.deleteFile(e, index, item.fileId)}>
                    <Icon className="close-icon" type="cross" />
                  </span>
                  <span className="upload-file-list-download" onClick={()=>{
                    downLoad(URL.GRT_URL, item.fileId) 
                  }}>
                    <Icon className="download-icon" type="export" />
                  </span>
                  <span  className="upload-file-list-preview" onClick={()=>{
                    visibilityFile(URL.PREVIEW, item)
                  }}>
                    <Icon className="preview-icon" type="eye" />
                  </span>
                  
                </a>
              )
            }
          </div>
        </div>
      </div>
    )
  }
}


// 备注:新的upload组件,支持批量上传功能, 支持下载和预览

// 逐步替换掉现有的老的组件

// 接受参数
// type="button" // 附件类型
// disabled = false, 
// shape="", 
// icon="plus", 
// btnType="primary", 
// size="", 
// text="上传", 
// className="",
// action, 
// multiple, 
// limit
// maxLength // 上传最大数

附上css

$success-color: #118bfb;
$error-color: red;
.c-upload-btn{
   // button 的upload组件
   .file-upload-type-button{
      .upload-file-list{
        font-size: 14px;
        display: block;
        margin:10px 10px 10px 0px;
        color: #333;
        padding: 5px;
        background: rgb(244,244,244);
        &-name{
            display: inline-block;
            color: rgba(0,0,0,0.87);
            width: -moz-calc(100% - 125px);
            width: -webkit-calc(100% - 125px);
            width: calc(100% - 125px);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            .report-icon{
                margin: 0 5px 0 5px;
            }
            &:hover {
                color: $success-color;
            }
        }
        &-close, &-download, &-preview{
            color: rgba(0,0,0,0.87);
            float: right;
            padding: 2px 5px;
            margin: 0 5px 0 0px;
            cursor: pointer;
            &:hover{
            color: $success-color;
            }
            .download-text, .preview-text{
            position: relative;
            top: -1px;
            left: 5px;
            }
        }
        // .success-text, .error-text{
        //     float: right;
        //     margin-right: 40px;
        //     .success-icon, .error-icon{
        //     margin-right: 5px;
        //     }
        // }
        // .success-text{
        //     color: $success-color;
        // }
        // .error-text{
        //     color: $error-color;
        // }
      }
   }  
}