javascript 应如何在 Ant Design Upload 组件中设置 customRequest 以使用 XMLHttpRequest?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47021594/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:07:44  来源:igfitidea点击:

How should customRequest be set in the Ant Design Upload component to work with an XMLHttpRequest?

javascriptreactjsxmlhttprequestantd

提问by Tyrannogina

I have a complete mess of a component. Right now I pass a function I have been trying a million things I can not make it work.

我有一个完全混乱的组件。现在我传递了一个函数,我一直在尝试一百万件我无法让它工作的事情。

export default class DatafileUpload extends Component {
  initialState = {
    fileUploading: false,
    fileList: [],
    status: 'empty', // 'empty' | 'active' | 'success' | 'exception'
    file: {}
  }

  state = this.initialState

  static propTypes = {
    userId: PropTypes.string.isRequired,
    datasetId: PropTypes.string.isRequired
  }

  scrubFilename = (filename) => filename.replace(/[^\w\d_\-.]+/ig, '')

  requestSignedS3Url = (file) => {
    const filename = this.scrubFilename(file.name)
    const params = {
      userId: this.props.userId,
      contentType: file.type,
      Key: `${filename}`
    };
    return api.get('/s3/signUpload', { params })
      .then(response => {
        return response.data;
      })
      .catch(error => {
        console.error(error);
      });
  }

  uploadFile = (file) => {
    this.requestSignedS3Url(file)
      .then(signResult => this.uploadToS3(file, signResult))
      .catch(error => console.log(error))
  }

  createCORSRequest = (method, url, opts) => {
    opts = opts || {};
    let xhr = new XMLHttpRequest();
    if (xhr.withCredentials != null) {
      xhr.open(method, url, true);
      if (opts.withCredentials != null) {
        xhr.withCredentials = opts.withCredentials;
      }
    } else if (typeof XDomainRequest !== "undefined") {
      xhr = new XDomainRequest();
      xhr.open(method, url);
    } else {
      xhr = null;
    }
    return xhr;
  };

  stepFunctions = () => {
    return {
      preprocess: (file) => {
        console.log('Pre-process: ' + file.name);
      },
      onProgress: (percent, message, file) => {
        this.setState({ fileUploading: true })
        console.log('Upload progress: ' + percent + '% ' + message);
      },
      onFinish: (signResult) => {
        this.setState({ fileUploading: false })
        console.log("Upload finished: " + signResult.publicUrl)
      },
      onError: (message) => {
        this.setState({ fileUploading: false })
        console.log("Upload error: " + message);
      },
      scrubFilename: (filename) => {
        return filename.replace(/[^\w\d_\-\.]+/ig, '');
      },
      onFinishS3Put: (signResult, file) => {
        console.log(signResult)
        return console.log('base.onFinishS3Put()', signResult.publicUrl);
      }
    }
  }

  uploadToS3 = async (file, signResult) => {
    const xhr = await this.createCORSRequest('PUT', signResult.signedUrl);
    const functions = this.stepFunctions()
    functions.preprocess(file)
    if (!xhr) {
      functions.onError('CORS not supported', file);
    } else {
      xhr.onload = () => {
        if (xhr.status === 200) {
          functions.onProgress(100, 'Upload completed', file);
          return functions.onFinishS3Put('potatopotato', file);
        } else {
          return functions.onError('Upload error: ' + xhr.status, file);
        }
      };
      xhr.onerror = () => {
        return functions.onError('XHR error', file);
      };
      xhr.upload.onprogress = (e) => {
        let percentLoaded;
        if (e.lengthComputable) {
          percentLoaded = Math.round((e.loaded / e.total) * 100);
          return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
        }
      };
    }
    xhr.setRequestHeader('Content-Type', file.type);
    if (signResult.headers) {
      const signResultHeaders = signResult.headers
      Object.keys(signResultHeaders).forEach(key => {
        const val = signResultHeaders[key];
        xhr.setRequestHeader(key, val);
      })
    }
    xhr.setRequestHeader('x-amz-acl', 'public-read');
    this.httprequest = xhr;
    return xhr.send(file);
  };

  handleChange = ({ file, fileList }) => {
    const functions = this.stepFunctions()
    functions.preprocess(file)
    if (!file) {
      functions.onError('CORS not supported', file);
    } else {
      file.onload = () => {
        if (file.status === 200) {
          functions.onProgress(100, 'Upload completed', file);
          return functions.onFinishS3Put('potatopotato', file);
        } else {
          return functions.onError('Upload error: ' + file.status, file);
        }
      };
      file.onerror = () => {
        return functions.onError('XHR error', file);
      };
      file.upload.onprogress = (e) => {
        let percentLoaded;
        if (e.lengthComputable) {
          percentLoaded = Math.round((e.loaded / e.total) * 100);
          return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
        }
      };
    }
    console.log('File: ', file)
    // always setState
    this.setState({ fileList });
  }

  render() {
    const props = {
      onChange: this.handleChange,
      multiple: true,
      name: "uploadFile",
      defaultFileList: this.initialState.fileList,
      data: this.uploadFile,
      listType: "text",
      customRequest: ????,
      showUploadList: {
        showPreviewIcon: true,
        showRemoveIcon: true
      },
      onProgress: ( {percent} ) => {
        this.setState({ fileUploading: true })
        console.log('Upload progress: ' + percent + '% ' );
      },
      onError: (error, body) => {
        this.setState({ fileUploading: false })
        console.log("Upload error: " + error);
      },
      onSuccess: (body)=> {
        console.log(body)
        return console.log('base.onFinishS3Put()');
      }
    };

    return (
      <Upload {...props} fileList={this.state.fileList}>
        <Button>
          <Icon type="upload" /> Upload
        </Button>
      </Upload>
    )
  }
}

I know this code is a mess that doesn't make sense and have duplicated data all around. I want it to make it work and then clean up/optimse. Basically I am not able to make the component progress bar update nor with the onChangenor when I am trying to use the customRequest. When is customRequestcalled? Thisis not very abundant in explanations... I don't understand how does it do the replacement of Ajax upload.

我知道这段代码是一团糟,没有意义并且到处都是重复的数据。我希望它让它工作,然后清理/优化。基本上,onChange当我尝试使用customRequest. 什么时候customRequest叫?这个解释不是很丰富……不明白它是怎么做的Ajax上传的替换。

回答by awdk

I was struggling with that as well and then I found your question.

我也在为此苦苦挣扎,然后我找到了你的问题。

So the way I found to use customRequest and onChange is:

所以我发现使用 customRequest 和 onChange 的方式是:

    <Upload name="file" customRequest={this.customRequest} onChange={this.onChange}>
      <Button>
        <Icon type="upload" /> Click to Upload
      </Button>
    </Upload>

  ...


  onChange = (info) => {
    const reader = new FileReader();
    reader.onloadend = (obj) => {
      this.imageDataAsURL = obj.srcElement.result;
    };
    reader.readAsDataURL(info.file.originFileObj);

    ...
  };

  ...

  customRequest = ({ onSuccess, onError, file }) => {
    const checkInfo = () => {
      setTimeout(() => {
        if (!this.imageDataAsURL) {
          checkInfo();
        } else {
          this.uploadFile(file)
            .then(() => {
              onSuccess(null, file);
            })
            .catch(() => {
              // call onError();
            });
        }
      }, 100);
    };

    checkInfo();
  };

There are probably better ways to do it, but I hope that helps you.

可能有更好的方法来做到这一点,但我希望对你有所帮助。

回答by U.Rush

I struggled it a lot and find an efficient way to handle this case.

我为此苦苦挣扎,并找到了一种有效的方法来处理这种情况。

first- you should mess with the customRequest only when you need to change to body and the request type (like using post instead of 'put' or using xml or add another extra header).

首先 - 只有当您需要更改正文和请求类型(例如使用 post 而不是 'put' 或使用 xml 或添加另一个额外的标头)时,您才应该使用 customRequest 。

for the signing Url you can send in the action prop callback which return a promise with the right Url to upload like:

对于签名 Url,您可以发送 action prop 回调,它会返回一个带有要上传的正确 Url 的承诺,例如:

 handleUplaod = (file: any) => {
return new Promise(async (resolve, reject) => {
  const fileName = `nameThatIwant.type`;
  const url = await S3Fetcher.getPresignedUrl(fileName);
  resolve(url);
});

and render like:

并渲染如下:

render(){
    return(
    ....
    <Upload
    action={this.handleUplaod}
    ....
 Upload>

the uploader take the url from the action prop.

上传者从动作道具中获取网址。

the onChange method which is provided also will be called any time the status of upload is changed-

提供的 onChange 方法也将在上传状态更改时调用 -

onChange# The function will be called when uploading is in progress, completed or failed.

When uploading state change, it returns:

{ file: { /* ... / }, fileList: [ /... / ], event: { /... */ }, }

onChange# 当上传正在进行、完成或失败时将调用该函数。

当上传状态改变时,它返回:

{ file: { /* ... / }, fileList: [ /... / ], event: { /... */ }, }

when upload started you will need to activate the file reader from that. like:

上传开始时,您需要从中激活文件阅读器。喜欢:

....
 fileReader = new FileReader();
 .....

onChange = (info) => {
    if (!this.fileReader.onloadend) {
      this.fileReader.onloadend = (obj) => {
        this.setState({
          image: obj.srcElement.result, //will be used for knowing load is finished.
        });
      };
    // can be any other read function ( any reading function from
    // previously created instance can be used )
    this.fileReader.readAsArrayBuffer(info.file.originFileObj);
    }
  };

notice when completed stage that event=undefind

请注意当完成阶段 event=undefin

To update the UI from the upload events you should use the options variables from customRequest and call them whenever you need.

要从上传事件更新 UI,您应该使用来自 customRequest 的选项变量,并在需要时调用它们。

onSuccess- should be called when you finish uploading and it will change the loading icon to the file name.

onSuccess- 应在您完成上传后调用,它将加载图标更改为文件名。

onError- will paint the file name filed to red.

onError- 将归档的文件名涂成红色。

onProgress- will update the progress bar and should be called with {percent: [NUMBER]} for updating.

onProgress- 将更新进度条,应使用 {percent: [NUMBER]} 调用以进行更新。

for example in my code-

例如在我的代码中-

customRequest = async option => {
  const { onSuccess, onError, file, action, onProgress } = option;
  const url = action;

  await new Promise(resolve => this.waitUntilImageLoaded(resolve)); //in the next section 
  const { image } = this.state; // from onChange function above
  const type = 'image/png';
  axios
    .put(url, Image, {
      onUploadProgress: e => {
        onProgress({ percent: (e.loaded / e.total) * 100 });
      },
      headers: {
        'Content-Type': type,
      },
    })
    .then(respones => {
      /*......*/
      onSuccess(respones.body);
    })
    .catch(err => {
      /*......*/
      onError(err);
    });
};

 waitUntilImageLoaded = resolve => {
  setTimeout(() => {
    this.state.image
      ? resolve() // from onChange method
      : this.waitUntilImageLoaded(resolve);
  }, 10);
};

I used axios but you can use other libraries as well and the most important part-

我使用 axios 但你也可以使用其他库和最重要的部分 -

render(){
return(
....
<Upload
onChange={this.onChange}
 customRequest={this.customRequest}
...>

回答by shuts13

  onCustomRequest = file => {
    return new Promise(((resolve, reject) => {
      const ajaxResponseWasFine = true;

      setTimeout(() => {
        if (ajaxResponseWasFine) {
          const reader  = new FileReader();

          reader.addEventListener('load', () => {
            resolve(reader.result);
          }, false);

          if (file) {
            reader.readAsDataURL(file);
          }
        } else {
          reject('error');
        }
      }, 1000);
    }));
  };
        <Dragger
          action={this.onCustomRequest}
          fileList={fileList}
          onChange={this.handleChangeUpload}
          className={styles.dragWrapper}
          showUploadList={true}
          beforeUpload={this.beforeUpload}
        >