Ошибка AWS S3 MalformedXML при выполнении операции PUT - PullRequest
1 голос
/ 17 октября 2019

Я пытаюсь воссоздать фрагмент кода для загрузки большого файла на S3. Я воспроизвел этот репозиторий .

К сожалению, при попытке загрузить даже небольшой файл я получаю ошибку MalformedXML. Кто-нибудь видит, чего мне не хватает?

Вот код бэкенда:

const express = require('express')
const app = express()
const BluebirdPromise = require('bluebird')
const AWS = require('aws-sdk')
const bodyParser = require('body-parser')

app.use(bodyParser.json())

const port = 4000
const BUCKET_NAME = "mybucket"

const s3  = new AWS.S3({
    accessKeyId: 'ACCESS_KEY_ID' , // Replace with your access key id
    secretAccessKey: 'SECRET_ACCESS_KEY' , // Replace with your secret access key
    s3ForcePathStyle: true,
    signatureVersion: 'v4'
});

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

app.get('/start-upload', async (req, res, next) => {
    try {
        let params = {
            Bucket: BUCKET_NAME,
            Key: req.query.fileName,
            ContentType: req.query.fileType
        }
        let createUploadPromised = BluebirdPromise.promisify(s3.createMultipartUpload.bind(s3))
        let uploadData = await createUploadPromised(params)
        res.send({uploadId: uploadData.UploadId})
    } catch(err) {
        console.log(err)
    }
})

app.get('/get-upload-url', async (req, res, next) => {
    try {
        let params = {
            Bucket: BUCKET_NAME,
            Key: req.query.fileName,
            PartNumber: req.query.partNumber,
            UploadId: req.query.uploadId
        }
        console.log(params)
        let uploadPartPromised = BluebirdPromise.promisify(s3.getSignedUrl.bind(s3))
        let presignedUrl = await uploadPartPromised('uploadPart', params)
        res.send({presignedUrl})
    } catch(err) {
        console.log(err)
    }
})

app.post('/complete-upload', async (req, res, next) => {
    try {
        console.log(req.body, ': body')
        let params = {
            Bucket: BUCKET_NAME,
            Key: req.body.params.fileName,
            MultipartUpload: {
                Parts: req.body.params.parts
            },
            UploadId: req.body.params.uploadId
        }
        console.log(params)
        let completeUploadPromised = BluebirdPromise.promisify(s3.completeMultipartUpload.bind(s3))
        let data = await completeUploadPromised(params)
        res.send({data})
    } catch(err) {
        console.log(err)
    }
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))

Вот код внешнего интерфейса:

import React, { Component } from 'react'
import axios from 'axios'

export default class Index extends Component {
    constructor(props) {
        super(props)
    this.state = {
      selectedFile: null,
      uploadId: '',
      fileName: '',
      backendUrl: 'http://localhost:4000'
    }
    }

  async fileChangedHandler(event) {
    try {
      // console.log('Inside fileChangedHandler')
      let selectedFile = event.target.files[0]
      let fileName = selectedFile.name
      this.setState({ selectedFile })
      this.setState({ fileName })
    } catch (err) {
      console.error(err, err.message)
    }
  }

  async startUpload(event) {
    try {
      // console.log('Inside startUpload')
      event.preventDefault()

      console.log(this.state.selectedFile.type + ' FileType')
      let resp = await axios.get(`${this.state.backendUrl}/start-upload`, {
        params: {
          fileName: this.state.fileName,
          fileType: this.state.selectedFile.type
        }
      })

      let {uploadId} = resp.data
      this.setState({uploadId})

      this.uploadMultipartFile()
    } catch(err) {
      console.log(err)
    }
  }

  async uploadMultipartFile() {
    try {
      console.log('Inside uploadMultipartFile')
      const FILE_CHUNK_SIZE = 10000000 // 10MB
      const fileSize = this.state.selectedFile.size
      const NUM_CHUNKS = Math.floor(fileSize / FILE_CHUNK_SIZE) + 1
      let promisesArray = []
      let start, end, blob

      for (let index = 1; index < NUM_CHUNKS + 1; index++) {
        start = (index - 1)*FILE_CHUNK_SIZE
        end = (index)*FILE_CHUNK_SIZE
        blob = (index < NUM_CHUNKS) ? this.state.selectedFile.slice(start, end) : this.state.selectedFile.slice(start)

        // (1) Generate presigned URL for each part
        let getUploadUrlResp = await axios.get(`${this.state.backendUrl}/get-upload-url`, {
          params: {
            fileName: this.state.fileName,
            partNumber: index,
            uploadId: this.state.uploadId
          }
        })

        let { presignedUrl } = getUploadUrlResp.data
        console.log('   Presigned URL ' + index + ': ' + presignedUrl + ' filetype ' + this.state.selectedFile.type)

        // (2) Puts each file part into the storage server
        let uploadResp = axios.put(
          presignedUrl,
          blob,
          { headers: { 'Content-Type': this.state.selectedFile.type } }
        )
        // console.log('   Upload no ' + index + '; Etag: ' + uploadResp.headers.etag)
        promisesArray.push(uploadResp)
      }

      let resolvedArray = await Promise.all(promisesArray)
      console.log(resolvedArray, ' resolvedAr')

      let uploadPartsArray = []
      resolvedArray.forEach((resolvedPromise, index) => {
        uploadPartsArray.push({
          ETag: resolvedPromise.headers.etag,
          PartNumber: index + 1
        })
      })

      // (3) Calls the CompleteMultipartUpload endpoint in the backend server

      let completeUploadResp = await axios.post(`${this.state.backendUrl}/complete-upload`, {
        params: {
          fileName: this.state.fileName,
          parts: uploadPartsArray,
          uploadId: this.state.uploadId
        }
      })

      console.log(completeUploadResp.data, ' Stuff')

    } catch(err) {
      console.log(err)
    }
  }

    render() {
        return (
      <div>
        <form onSubmit={this.startUpload.bind(this)}>
          <div>
            <p>Upload Dataset:</p>
            <input type='file' id='file' onChange={this.fileChangedHandler.bind(this)} />
            <button type='submit'>
              Upload
            </button>
          </div>
        </form>
      </div>
        )
    }
}

Вот вывод:

Example app listening on port 4000!
{
  Bucket: 'mybucket',
  Key: 'myobject.jpg',
  PartNumber: '1',
  UploadId: 'wCFRpYX96oG4sQaAAKVmP_4.r0Z5cxWsTUD9U.UDuHxNhQImMIMwUNEeJY_VNrfn5e5YflO4j9Uyo9wb611bwXbu_HQg9TpvJCmFIPZqAE6V_dJFfzsmKYCba5KsFALw'
}
{
  params: {
    fileName: 'myobject.jpg',
    parts: [ [Object] ],
    uploadId: 'wCFRpYX96oG4sQaAAKVmP_4.r0Z5cxWsTUD9U.UDuHxNhQImMIMwUNEeJY_VNrfn5e5YflO4j9Uyo9wb611bwXbu_HQg9TpvJCmFIPZqAE6V_dJFfzsmKYCba5KsFALw'
  }
} : body
{
  Bucket: 'mybucket',
  Key: 'myobject.jpg',
  MultipartUpload: { Parts: [ [Object] ] },
  UploadId: 'wCFRpYX96oG4sQaAAKVmP_4.r0Z5cxWsTUD9U.UDuHxNhQImMIMwUNEeJY_VNrfn5e5YflO4j9Uyo9wb611bwXbu_HQg9TpvJCmFIPZqAE6V_dJFfzsmKYCba5KsFALw'
}

MalformedXML: The XML you provided was not well-formed or did not validate against our published schema
    at Request.extractError (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/services/s3.js:583:35)
    at Request.callListeners (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
    at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:38:9) {
  cause: MalformedXML: The XML you provided was not well-formed or did not validate against our published schema
      at Request.extractError (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/services/s3.js:583:35)
      at Request.callListeners (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
      at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
      at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:683:14)
      at Request.transition (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:22:10)
      at AcceptorStateMachine.runTo (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:14:12)
      at /home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:26:10
      at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:38:9)
      at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:685:12)
      at Request.callListeners (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:116:18)
      at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
      at Request.emit (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:683:14)
      at Request.transition (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:22:10)
      at AcceptorStateMachine.runTo (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:14:12)
      at /home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/state_machine.js:26:10
      at Request.<anonymous> (/home/aws-s3-multipart-presigned-upload/backend/node_modules/aws-sdk/lib/request.js:38:9) {
    message: 'The XML you provided was not well-formed or did not validate against our published schema',
    code: 'MalformedXML',
    region: null,
    time: 2019-10-17T13:32:46.655Z,
    requestId: 'E275D080E8B60488',
    extendedRequestId: '5zOuxT+sl/BOOOn+pmgHjOO4jUI3YtrYC4KeXJKwwl9ID2mCzb997cF66hI1A03ZjHCQq0dhDx4=',
    cfId: undefined,
    statusCode: 400,
    retryable: false,
    retryDelay: 58.95911735466519
  },
  isOperational: true,
  code: 'MalformedXML',
  region: null,
  time: 2019-10-17T13:32:46.655Z,
  requestId: 'E275D080E8B60488',
  extendedRequestId: '5zOuxT+sl/BOOOn+pmgHjOO4jUI3YtrYC4KeXJKwwl9ID2mCzb997cF66hI1A03ZjHCQq0dhDx4=',
  cfId: undefined,
  statusCode: 400,
  retryable: false,
  retryDelay: 58.95911735466519
}

Спасибо за помощь!

...