web-dev-qa-db-fra.com

Comment CustomRequest doit-il être défini dans le composant Ant Design Upload pour fonctionner avec un XMLHttpRequest?

J'ai un désordre complet d'un composant. En ce moment, je passe une fonction que j'ai essayé un million de choses que je ne peux pas faire fonctionner.

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

Je sais que ce code est un gâchis qui n'a pas de sens et qui contient des données en double tout autour. Je veux que cela fonctionne, puis nettoyer/optimiser. Fondamentalement, je ne suis pas en mesure de mettre à jour la barre de progression du composant ni avec le onChange ni lorsque j'essaie d'utiliser le customRequest. Quand est appelé customRequest? This n'est pas très abondant dans les explications ... Je ne comprends pas comment cela fait le remplacement du téléchargement Ajax.

9
Tyrannogina

Je me débattais aussi avec cela, puis j'ai trouvé votre question.

Donc, la façon dont j'ai trouvé d'utiliser customRequest et onChange est:

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

Il existe probablement de meilleures façons de le faire, mais j'espère que cela vous aidera.

9
awdk

Je me suis beaucoup battu et j'ai trouvé un moyen efficace de gérer cette affaire.

tout d'abord, vous ne devez jouer avec customRequest que lorsque vous devez modifier le corps et le type de demande (comme utiliser post au lieu de 'put' ou utiliser xml ou ajouter un autre en-tête supplémentaire).

pour l'URL de signature, vous pouvez envoyer le rappel d'action prop qui renvoie une promesse avec l'URL appropriée à télécharger comme:

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

et rendre comme:

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

l'uploader prend l'url de l'action prop.

la méthode onChange qui est également fournie sera appelée chaque fois que le statut du téléchargement est modifié-

onChange # La fonction sera appelée lorsque le téléchargement est en cours, terminé ou a échoué.

Lors du téléchargement d'un changement d'état, il renvoie:

{fichier: {/ * ... /}, fileList: [/ ... /], événement: {/ ... * /},}

lorsque le téléchargement a commencé, vous devrez activer le lecteur de fichiers à partir de là. comme:

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

remarquez à la fin de l'étape que l'événement = undefind

Pour mettre à jour l'interface utilisateur à partir des événements de téléchargement, vous devez utiliser les variables d'options de customRequest et les appeler chaque fois que vous en avez besoin.

onSuccess- doit être appelé lorsque vous avez terminé le téléchargement et il changera l'icône de chargement en nom de fichier.

onError- peindra le nom de fichier classé en rouge.

onProgress- mettra à jour la barre de progression et devrait être appelé avec {percent: [NUMBER]} pour la mise à jour.

par exemple dans mon 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);
};

J'ai utilisé axios mais vous pouvez aussi utiliser d'autres bibliothèques et le plus important -

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