【上传组件】react-dropzone-component VS react-filepond

1. react-dropzone-component


 

写一个名为MultiFilesUpload.js组件

// @flow
import React from 'react';
import Dropzone from 'react-dropzone-component';
import { Button, Icon } from 'semantic-ui-react';
import { withAlert } from 'react-alert';
import withFetch from './hoc/withFetch';

type Props= {
  uploadUrl: string,
  disabled: boolean,
  minHeight?: string,
  message: string,
  fileType: string,
  hideBtn: boolean, // 是否隐藏按钮
  maxFiles: number, // 上传的最大文件数
  contentFiles: number, // 上传的当前文件数
  alert: () => void,
  alerts: () => void,
  acceptedFiles: string, // 限制文件格式
  btnMeg?: string, // 按钮的文案, 默认为上传
  btnClassName: string, // 按钮的类名, 支持父元素传递的样式
  onFileUploaded: (result: Object) => void,
  onFileDeleted: (file: Object) => void,
  onLoading: (onLoading: boolean) => void,
}

type State= {
  onLoading: boolean,
}

export class MultiFilesUpload extends React.PureComponent<Props, State> {
  defaultProps: Props
  maxFilesExceeded: (file: Object) => void
  handleUpload: Function
  handleRemove: (file: Object) => void
  eventHandlers: Object
  dropzone: Object
  djsConfig: Object

  static defaultProps = {
    uploadUrl: 'k2data.com',
    minHeight: '36px',
    maxFiles: 60,
    disabled: false,
    btnMeg: '上传',
  }

  constructor(props: Props) {
    super(props);

    this.state = {
      onLoading: false,
    };

    this.maxFilesExceeded = this.maxFilesExceeded.bind(this);
    this.handleUpload = this.handleUpload.bind(this);
    this.handleRemove = this.handleRemove.bind(this);
    this.djsConfig = {
      autoProcessQueue: false,
      addRemoveLinks: true,
      acceptedFiles: this.props.acceptedFiles,
      maxFiles: props.maxFiles,
      dictRemoveFile: '删除文件',
    };
    this.eventHandlers = {
      init: (dropzone) => { this.dropzone = dropzone; },
      // maxfilesexceeded: this.maxFilesExceeded,
      addedfile: this.handleUpload,
      removedfile: this.handleRemove,
    };
  }

  maxFilesExceeded(file: Object) {
    this.dropzone.removeFile(file);
    if (this.props.alert) {
      this.props.alert.error('达到最大上传文件数', {
        timeout: 3000,
      });
    } else {
      this.props.alerts.error('达到最大上传文件数', {
        timeout: 3000,
      });
    }
  }

  handleUpload = async (file) => {
    const {
      onFileUploaded, fileType = 'contract', contentFiles, maxFiles,
    } = this.props;
    // 限制文件大小
    // if (size > 300000) {
    //   this.props.alert.error('上传文件大小超限');
    //   return;
    // }
    // 到达最大个数后不执行上传
    if (file.name.indexOf('&') !== -1 || file.name.indexOf('/') !== -1 || file.name.indexOf('\\') !== -1) {
      if (this.props.alert) {
        this.props.alert.error('不能含有特殊字符"&"/""\\"');
      } else {
        this.props.alerts.error('不能含有特殊字符"&"/""\\"');
      }
      return;
    }
    if (contentFiles && contentFiles >= maxFiles) {
      if (this.props.alert) {
        this.props.alert.error('达到最大上传文件数');
      } else {
        this.props.alerts.error('达到最大上传文件数');
      }
      return;
    }
    this.setState({ onLoading: true });
    if (this.props.onLoading) {
      this.props.onLoading(true);
    }
    const data = new FormData();
    data.append(`${fileType}`, file);
    const body = {
      method: 'POST',
      body: data,
    };
    const { fetchWithUser, handleFetchError } = this.props;
    try {
      const json = await fetchWithUser(this.props.uploadUrl, body);
      // eslint-disable-next-line
      file.response = json
      if (typeof onFileUploaded === 'function') {
        onFileUploaded(json);
        this.setState({ onLoading: false });
        if (this.props.onLoading) {
          this.props.onLoading(false);
        }
      }
    } catch (e) {
      const mes = `上传失败:${e.message}`;
      if (this.props.alert) {
        this.props.alert.error(mes, {
          timeout: 3000,
        });
      } else {
        this.props.alerts.error(mes, {
          timeout: 3000,
        });
      }
      this.setState({ onLoading: false });
      if (this.props.onLoading) {
        this.props.onLoading(false);
      }
      handleFetchError(e);
    }
  }

  handleRemove(file: Object) {
    const { onFileDeleted } = this.props;
    if (typeof onFileDeleted === 'function') {
      onFileDeleted(file.response);
    }
  }

  render() {
    const {
      minHeight, message, disabled, btnMeg, btnClassName, hideBtn,
    } = this.props;
    const { onLoading } = this.state;
    return (
      <div className="fileInputCon">
        <div style={{ position: 'relative', display: 'inline-block' }}>
          {
            hideBtn ? ''
            : (<Button
              basic
              className={btnClassName || 'uploadBtn'}
              disabled={disabled || onLoading}
            >
              {btnMeg}
              {
                !hideBtn && onLoading
                ? <Icon name="spinner" className="splnnerLoading" />
                : ''
              }
            </Button>)
          }
          {
            // !hideBtn && onLoading
            // ? <div className="upload-loader"><Loader active /></div>
            // : ''
          }
          {
            disabled || onLoading ? '' : (
              <div className="container">
                <Dropzone
                  accept="image/jpeg, image/png"
                  eventHandlers={this.eventHandlers}
                  config={{
                    showFiletypeIcon: true,
                    postUrl: 'no-url',
                  }}
                  djsConfig={this.djsConfig}
                />
              </div>
            )
          }
        </div>
        <span className="updataMes">{message}</span>
        <style jsx>{`
            .fileInputCon{
              position: relative;
              display: inline-block;
            }
            .container {
              position: absolute;
              left: 0;
              right: 0;
              top: 0;
              bottom: 0;
              opacity: 0;
            }
            .updataMes{
              color: #999;
              font-size: 12px;
            }
            .container :global(.filepicker.dropzone) {
              position: relative;
              text-align: center;
              padding: 10px;
              border: 1px dashed rgba(0,0,0,.1);
              border-radius: 4px;
              background: transparent;
              min-height: ${minHeight || '36px'};
              pointer-events: ${disabled ? 'none' : 'auto'};
            }
            .container :global(
              .dropzone .dz-preview.dz-error .dz-error-message,
              .dropzone .dz-preview.dz-error .dz-error-mark,
              .dropzone .dz-preview .dz-progress,
              .container .dropzone .dz-message,
              .dropzone .dz-preview
            ) {
              display: none;
            }
        `}
        </style>
        <style global jsx>{`
            button.ui.button.uploadBtn {
              color: #009edc!important;
              border: 1px solid #009edc;
              box-shadow: none;
              padding: 8px 0;
              width: 100px;
            }
            @keyframes button-spin {
              from {
                -webkit-transform: rotate(0deg);
                        transform: rotate(0deg);
              }
              to {
                -webkit-transform: rotate(360deg);
                        transform: rotate(360deg);
              }
            }
            i.icon.splnnerLoading{
              margin-left: 5px!important;
              -webkit-animation: button-spin 0.8s linear;
              animation: button-spin 0.8s linear;
              -webkit-animation-iteration-count: infinite;
              animation-iteration-count: infinite;
            }
        `}
        </style>
      </div>
    );
  }
}

export default withFetch(withAlert(MultiFilesUpload));

页面中使用

import MultiFilesUpload from '../MultiFilesUpload';


<MultiFilesUpload
     uploadUrl={`${env.SERVICE_URL}/services/upload`}
     message="(建议尺寸100px*100px)"
     fileType="file"
     disabled={coverImg}
     acceptedFiles="image/*"
     onFileUploaded={this.handleCoverUpload}
     onFileDeleted={this.handleCoverDelete}
     maxFiles={1}
     contentFiles={coverImg ? 1 : 0}
    />
                          

2. react-filepond


 

写一个Filepond.js组件

// @flow
import React from 'react';
import { FilePond, File } from 'react-filepond';
import { connect } from 'react-redux';
import { withAlert } from 'react-alert';

type Props = {
  uploadUrl: string,
  token: string,
  fileType: string,
  deletUrl: string,
  onUploaded: Function,
  files: Array<Object>,
  onError?: Function,
  message?: string,
  alert: Object,
  onFileUploaded: Function, // 上传功能后执行的函数
}

type State = {
  files: Array<File>,
  progress: number,
}

export class FilepondUpload extends React.PureComponent<Props, State> {
  pond: any
  state = {
    files: [],
    progress: 1,
  }

  onprocessfileprogress = (file, progress) => {
    const { onUploaded } = this.props;
    this.setState({ progress });
    setTimeout(() => {
      if (onUploaded && progress === 1) {
        onUploaded();
      }
    }, 1000);
  }

  uploadConfig() {
    const { uploadUrl, token, deletUrl } = this.props;
    return {
      url: '',
      process: {
        url: uploadUrl,
        withCredentials: false,
        headers: {
          Authorization: `Bearer ${token}`,
        },
        onload: this.onload,
        onerror: this.onerror,
      },
      revert: {
        url: deletUrl,
        withCredentials: false,
        headers: {
          Authorization: `Bearer ${token}f`,
        },
      },
    };
  }

  onload = (e) => {
    console.log('上传成功后的response', e);
    if (this.props.onFileUploaded) {
      this.props.onFileUploaded(JSON.parse(e));
    }
  }
  onerror = (e) => {
    console.log('上传失败后的response', e);
  }


  handleError = (e) => {
    this.setState({ progress: 1 });
    if (this.props.onError) {
      this.props.onError(e);
    }
  }

  addFile = (e, file) => {
    const { filename } = file;
    if (filename.indexOf('&') !== -1 || filename.indexOf('/') !== -1 || filename.indexOf('\\') !== -1) {
      this.props.alert.error('不能含有特殊字符"&"/""\\"');
      file.abortProcessing();
      file.abortLoad();
      return null;
    }
  }
  processFile = (file) => {
    const { filename } = file;
    if (filename.indexOf('&') !== -1 || filename.indexOf('/') !== -1 || filename.indexOf('\\') !== -1) {
      file.abortProcessing();
      file.abortLoad();
      return null;
    }
  }

  render() {
    const { files, progress } = this.state;
    const { fileType, message = '上传合同' } = this.props;
    const { onprocessfileprogress } = this;
    return (
      <div className="container-wraper">
        <div className="container">
          <div className="progress" >
            <progress value={progress} max="1" className="progress-bar" />
            <span>{parseInt(progress * 100, 10)}%</span>
          </div>
          <div className="upload">
            <FilePond {...{
              maxFiles: 100,
              labelIdle: message,
              server: this.uploadConfig(),
              name: fileType,
              allowMultiple: true,
              allowRevert: false,
              instantUpload: true,
              onprocessfileprogress,
              onerror: this.handleError,
              onaddfile: this.addFile,
              onprocessfilestart: this.processFile,
            }}
            >
              {files.map(file => (<File source={file} key={file} />))}
            </FilePond>
          </div>
        </div>

        <style jsx>{`
          .container-wraper {
            display: inline-block;
            width: 130px;
            height: 40px;
            cursor: pointer;
          }
          .container {
            position: relative;
          }
          .progress {
            position: absolute;
            top: 2px;
            line-height: 32px;
            z-index: ${progress === 1 ? 1 : 10};
            opacity: ${progress === 1 ? 0 : 1};
            height: 32px;
            width: 123px;
            left: 2px;
            background-color: white;
          }
          .progress-bar {
            width: 90px;
            transform: translateY(3px);
          }
          .upload {
            position: absolute;
            top: 0;
            z-index: 2;
            height: 33px;
          }
          .upload :global(.filepond--list-scroller) {
            transform:translate3d(0px, 34px, 0) !important;
            opacity: 0;
          }
          .upload:active{
            background: #009edc;
          }
          .text {
            position: absolute;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
          }
        `}
        </style>
        <style jsx global>{`
          .filepond--root {
            height: 35px !important;
            overflow: hidden;
            border: 1px solid #009edc;
            width: 126px;
            z-index: 2;
            border-radius: 2px !important;
          }
          .filepond--browser {
            display: none;
          }
          .filepond--drop-label {
            opacity: 1 !important;
            transform: translate3d(0px, 0, 0) !important;
            visibility: visible !important;
            cursor: pointer;
            pointer-events: auto !important; // FIXME: allow multiple upload one by one
          }
          .filepond--drop-label label {
            line-height: 33px;
            padding: 0;
          }
          .filepond--drop-label label {
            cursor: pointer;
          }
          .filepond--drop-label label:active {
            color: white;
          }
          .filepond--file-action-button {
            cursor: pointer;
          }
          .filepond--drip, .filepond--drop-label, .filepond--panel {
            background: transparent !important;
            border-radious: 0;
            color: #009edc;
          }
          .filepond--drip, .filepond--panel-root {
            display: none;
          }
        `}
        </style>
      </div>
    );
  }
}
const mapStateToProps = state => ({
  token: state.user.token,
});

export default withAlert(connect(mapStateToProps, {})(FilepondUpload));

页面中使用

import FilepondUpload from './Filepond';

<FilepondUpload
  uploadUrl={UPLOAD_URL}
  message="上传合同"
  fileType="contract"
  onUploaded={onUploaded}
  onError={this.repeatFileName}
  />

 

posted @ 2018-09-10 16:01  簌大侠  阅读(1294)  评论(0编辑  收藏  举报