【问题标题】:Send ArrayBuffer to S3 put to signedURL将 ArrayBuffer 发送到 S3 放到 signedURL
【发布时间】:2019-10-03 14:27:24
【问题描述】:

我正在逐步将文件加载到缓冲区中,缓冲区是有效的,但是当 ArrayBuffer 完成将文件加载到其中时,浏览器会崩溃。我需要做的是能够将缓冲区buf = this.concatBuffers(buf, buffer); 的片段发送到 axios PUT 请求,以便我可以逐步将文件上传到 s3,而不是将其加载到 promise 返回的单个变量中(作为内存超出)。

如何修改 readFileAsBuffer 和 uploadFileToS3 方法之间的链接来做到这一点?

这是我的代码,您可以按照流程进行操作。

concatTypedArrays = (a, b) => {
  const c = new a.constructor(a.length + b.length);
  c.set(a, 0);
  c.set(b, a.length);
  return c;
};

concatBuffers = (a, b) =>
  this.concatTypedArrays(
    new Uint8Array(a.buffer || a),
    new Uint8Array(b.buffer || b),
  ).buffer;

readFileAsBuffer = file =>
  new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.file = file;
    let buf = new ArrayBuffer();
    const fileChunks = new FileChunker(file, 2097152);

    fileReader.readAsArrayBuffer(fileChunks.blob());

    fileReader.onload = e => {
      this.onProgress(fileChunks);
      const buffer = e.target.result;
      buf = this.concatBuffers(buf, buffer);

      if (fileChunks.hasNext()) {
        fileChunks.next();
        fileReader.readAsArrayBuffer(fileChunks.blob());
        return;
      }

      resolve(buf);
    };

    fileReader.onerror = err => {
      reject(err);
    };
  });

uploadFileToS3 = fileObject => {
  new Promise((resolve, reject) => {
    const decodedURL = decodeURIComponent(fileObject.signedURL);

    this.readFileAsBuffer(fileObject.fileRef).then(fileBuffer => {
      console.log(fileBuffer);
      axios
        .put(decodedURL, fileBuffer, {
          headers: {
            'Content-Type': fileObject.mime,
            'Content-MD5': fileObject.checksum,
            'Content-Encoding': 'UTF-8',
            'x-amz-acl': 'private',
          },
          onUploadProgress: progressEvent => {
            const { loaded, total } = progressEvent;
            const uploadPercentage = parseInt(
              Math.round((loaded * 100) / total),
              10,
            );
            this.setState({ uploadProgress: uploadPercentage });
            console.log(`${uploadPercentage}%`);

            if (uploadPercentage === 100) {
              console.log('complete');
            }
          },
        })
        .then(response => {
          resolve(response.data);
        })
        .catch(error => {
          reject(error);
        });
    });
  });
};

uploadAllFilesToS3 = () => {
  const { files } = this.state;
  new Promise((resolve, reject) => {
    Object.keys(files).map(idx => {
      this.uploadFileToS3(files[idx])
        .then(response => {
          this.setState({ files: [] });
          resolve(response.data);
        })
        .catch(error => {
          reject(error);
        });
    });
  });
};

calcFileMD5 = file =>
  new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.file = file;
    const spark = new SparkMD5.ArrayBuffer();
    const fileChunks = new FileChunker(file, 2097152);

    fileReader.readAsArrayBuffer(fileChunks.blob());

    fileReader.onload = e => {
      this.onProgress(fileChunks);
      const buffer = e.target.result;
      spark.append(buffer);

      if (fileChunks.hasNext()) {
        fileChunks.next();
        fileReader.readAsArrayBuffer(fileChunks.blob());
        return;
      }

      const hash = spark.end();
      const checksumAWS = Buffer.from(hash, 'hex').toString('base64');

      resolve(checksumAWS);
    };

    fileReader.onerror = err => {
      reject(err);
    };
  });

【问题讨论】:

    标签: javascript amazon-s3 ecmascript-6 filereader aws-sdk-js


    【解决方案1】:

    我最终不需要创建自己的文件缓冲区,而是如果我将输入返回的 fileReference 直接发布到 axios(或 xhr),请求会自动分块上传。

    最初我只能让它与 XMLHttpRequest 一起工作,但我很快找到了一种将它包裹在 axios 周围的方法,从而简化了逻辑。

    XMLHttpRequest
    const xhr = createCORSRequest('PUT', url);
    
    if (!xhr) {
      console.log('CORS not supported');
    } else {
      xhr.onload = function(){
        if(xhr.status == 200) {
          console.log('completed');
        } else {
          console.log('Upload error: ' + xhr.status);
        }
      };
    
      xhr.onerror = function(err) {
        console.log(err)
      };
    
      xhr.upload.onprogress = function(progressEvent){
       console.log(progressEvent);
      };
    
      xhr.setRequestHeader('Content-Type', file.type);
      xhr.setRequestHeader('Content-MD5', md5_base64_binary);
      xhr.setRequestHeader('Content-Encoding', 'UTF-8');
      xhr.setRequestHeader('x-amz-acl', 'private');
      xhr.send(file);
    }
    

    或者使用axios;

    uploadFileToS3 = fileObject => {
      return new Promise((resolve, reject) => {
        const { enqueueSnackbar } = this.props;
        const decodedURL = decodeURIComponent(fileObject.signedURL);
    
        axios
          .put(decodedURL, fileObject.fileRef, {
            headers: {
              'Content-Type': fileObject.mime,
              'Content-MD5': fileObject.checksum,
              'Content-Encoding': 'UTF-8',
              'x-amz-acl': 'private',
            },
            onUploadProgress: progressEvent => {
              const { loaded, total } = progressEvent;
              const uploadPercentage = parseInt(
                Math.round((loaded * 100) / total),
                10,
              );
              this.setState({ uploadProgress: uploadPercentage });
            },
          })
          .then(response => {
            resolve(response.data);
          })
          .catch(error => {
            reject(error);
          });
      });
    };
    

    【讨论】:

      【解决方案2】:

      您是否尝试过使用 formData 上传文件?让浏览器处理文件读取。

      const data = new FormData()
      data.append('file', file)
      
      axios.put(decodedURL, data, ....)
      

      另一种选择是使用 axios https://github.com/axios/axios#request-config transformRequest 属性。并要求在那里读取文件。

      【讨论】:

        猜你喜欢
        • 2019-02-15
        • 1970-01-01
        • 2021-07-12
        • 2019-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-04
        • 1970-01-01
        相关资源
        最近更新 更多