web-dev-qa-db-ja.com

XMLHttpRequestを使用するには、Ant Design UploadコンポーネントでcustomRequestをどのように設定する必要がありますか?

コンポーネントの完全な混乱があります。現在、関数を渡していますが、機能させることができない数百万のことを試みています。

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>
    )
  }
}

私はこのコードが意味をなさない混乱であることを知っており、データがすべて重複しています。私はそれがそれを機能させて、それからクリーンアップ/最適化したいです。基本的に、コンポーネントの進行状況バーを更新したり、onChangeを使用したり、customRequestを使用しようとしたりすることはできません。 customRequestはいつ呼び出されますか? これ は説明があまり豊富ではありません... Ajaxアップロードの置き換えをどのように行うのかわかりません。

9
Tyrannogina

私もそれに苦労していて、あなたの質問を見つけました。

したがって、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();
  };

おそらくもっと良い方法がありますが、それがあなたを助けることを願っています。

9
awdk

私はそれをかなり苦労し、このケースを処理する効率的な方法を見つけました。

最初に、本文とリクエストタイプに変更する必要がある場合にのみ、customRequestをいじる必要があります(「put」の代わりにpostを使用するか、xmlを使用するか、別のヘッダーを追加するなど)。

署名するURLの場合、次のようにアップロードするための適切なURLを持つpromiseを返すアクションプロップコールバックで送信できます。

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

次のようにレンダリングします:

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

アップローダーはアクションプロップからURLを取得します。

提供されているonChangeメソッドも、アップロードのステータスが変更されるたびに呼び出されます-

onChange#この関数は、アップロードが進行中、完了、または失敗したときに呼び出されます。

状態変更をアップロードすると、次が返されます。

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

アップロードが始まったら、そこからファイルリーダーをアクティブにする必要があります。お気に入り:

....
 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);
    }
  };

ステージが完了すると、event = undefindであることに注意してください

アップロードイベントからUIを更新するには、customRequestのオプション変数を使用して、必要なときにいつでも呼び出す必要があります。

アップロードが完了するとonSuccess-が呼び出され、読み込みアイコンがファイル名に変わります。

onError-は、ファイルされたファイル名を赤にペイントします。

onProgress-は進行状況バーを更新するため、更新する場合は{percent:[NUMBER]}で呼び出す必要があります。

たとえば私のコードでは

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);
};

私はaxiosを使用しましたが、他のライブラリも使用でき、最も重要な部分-

render(){
return(
....
<Upload
onChange={this.onChange}
 customRequest={this.customRequest}
...>
3
U.Rush
  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}
        >
0
shuts13