【问题标题】:Cannot pipe s3 object readstream to PUT request无法通过管道将 s3 对象读取流传输到 PUT 请求
【发布时间】:2017-03-18 17:48:47
【问题描述】:

我有一个简单的场景。我需要从 S3 读取一个对象并将其输出通过管道传输到 PUT 请求。这是我使用request 的代码。

// client.js
let AWS = require('aws-sdk')
let request = require('request')

let bucket = 'my_bucket'
let filename = 'path/to/file.zip'

let host = 'localhost'
let port = 8080

let s3 = new AWS.S3({
  . . .
})

let readStream = s3.getObject({
  Bucket: bucket,
  Key: filename
}).createReadStream()

let formData = {
  applicationType: 'my_app_type',
  applicationName: 'my_app_name',
  upload: {
    value: readStream,
    options: {
      filename: 'my_file_name.zip',
      contentType: 'application/zip'
    }
  }
}

request.put({
  url: 'http://' + host + ':' + port + '/bootstrap',
  formData: formData
}, function (error, response, body) {
  if (error) throw error
  console.log(body)
})

还有,这是我的server.js 代码。

// server.js
let http = require('http')
let Busboy = require('busboy')
let events = require('events')
let fs = require('fs')

let host = 'localhost'
let port = 8080

let compressedCodeLocation = './code.zip'

let handleRequest = function (request, response) {
  let eventEmitter = new events.EventEmitter()
  let inputStreamWriter = fs.createWriteStream(compressedCodeLocation)
  inputStreamWriter.on('finish', function () {
    eventEmitter.emit('input.stream.saved')
  })
  let busboy = new Busboy({
    headers: request.headers
  })

  busboy.on('file', function (field, file) {
    file.pipe(inputStreamWriter)
  })
  busboy.on('field', function (field, val) {
    console.log(field + ': ' + val)
  })
  eventEmitter.on('input.stream.saved', function () {
    let stats = fs.statSync(compressedCodeLocation)
    response.statusCode = 200
    response.end(JSON.stringify(stats))
  })

  request.pipe(busboy)
}

let server = http.createServer(handleRequest)
server.listen(port, host, function () {
  console.log('Server started on ' + host + ':' + port)
})

let handleShutdown = function () {
  server.close(function () {
    console.log('Server stopped on ' + host + ':' + port)
  })
}
process.on('SIGTERM', handleShutdown)
process.on('SIGINT', handleShutdown)

Server 收到以下headers

{ host: 'localhost:8080',
  'content-type': 'multipart/form-data; boundary=--------------------------870259812928253745629174',
  'content-length': '465',
  connection: 'close' }

我在Server 端收到此错误:

File [upload] got 58 bytes
events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: Unexpected end of multipart data
    at /pots/cnc/node_modules/dicer/lib/Dicer.js:62:28
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickCallback (internal/process/next_tick.js:98:9)

并且,Client 收到以下错误:

Error: read ECONNRESET
    at exports._errnoException (util.js:1018:11)
    at TCP.onread (net.js:568:26)

有趣的是,如果我先在本地保存文件,然后为该本地文件保存createReadStream,它可以工作:

let formData = {
  ...
  upload: {
    value: fs.createReadStream(localPath + "/" + filename),
    options: {
      ...
    }
  }
};

【问题讨论】:

  • @TarunLalwani 我今晚会试一试,如果它有效,会更新你。谢谢:)
  • @TarunLalwani 不幸的是它没有用。前导和后导标志对multipart/form-data 没有影响,我也尝试将请求转换为multipart/related,但busboy 不支持。
  • 错误发生在服务器端还是客户端?您还可以打印请求附带的标题并将详细信息添加到您的问题中吗?
  • 我认为 s3.getObject().createReadStream() 有问题 - 我遇到了类似的行为... request.put 对本地文件流很好,但对 s3 流失败...我还尝试将 s3 流通过管道传输到本地文件,并且工作正常...

标签: node.js request node-request busboy


【解决方案1】:

我在使用请求时遇到了问题。但工作得很好。试试这个:

const {S3} = require('aws-sdk'),
      got = require('got'),
      FormData = require('form-data'),
;
const form = new FormData(),
      readStream = s3.getObject({
          Bucket: bucket,
          Key: filename
      }).createReadStream()
;
form.append('applicationType', 'my_app_type')
form.append('applicationName', 'my_app_name')
form.append('upload', readStream,{
  filename: 'my_file_name.zip',
  contentType: 'application/zip'
})
got.put('http://' + host + ':' + port + '/bootstrap', {body: form})

【讨论】:

  • 完美@metakungfu,成功了!!!请给我一些时间做更多的测试,并理解这个got 模块。在您的研究中,您是否发现 request 模块有任何问题?我们与request 非常相关,因此该模块的答案将对我们有很大帮助。谢谢你。 :)
  • 我通过您的帮助和request 模块本身解决了我的问题。我不确定它为什么起作用的原因。也许你能帮忙?
  • 我不知道 - 我在 lambda 中调用了类似的东西,所以很容易就可以使用任何工作。没有时间深入研究它。可能应该在请求 repo 上提交票证。
【解决方案2】:

解决方案 2:

我今天找到的另一个答案要简单得多。只需使用属性knownLength 让请求模块提前知道文件的大小。

upload: {
  value: readStream,
  options: {
    filename: 'my_file_name.zip',
    contentType: 'application/zip'
    knownLength: 423424
  }
}


解决方案 1

此解决方案使用 request 模块本身,以防万一 不想在您的代码中引入新库。

我在@metakungfu answer 的帮助下解决了这个问题。请将任何投票重定向到他的回答。

我需要将require 模块的表单重置为form-data 模块的表单。正如request documentation 所说:

对于高级情况,您可以通过以下方式访问表单数据对象本身 r.form().

一旦在我的请求中设置了新表单(我认为这会重写多部分边界),我会将表单数据通过管道传递给我的请求。 See Reference.

let AWS = require('aws-sdk')
let FormData = require('form-data')
let request = require('request')

let bucket = 'ppi-uploads'
let filename = 'introduction.zip'

let host = 'localhost'
let port = 8080

let s3 = new AWS.S3({
  . . .
})

let readStream = s3.getObject({
  Bucket: bucket,
  Key: filename
}).createReadStream()

let form = new FormData()
form.append('applicationType', 'html')
form.append('applicationName', 'introduction')
form.append('upload', readStream, {
  filename: 'introduction.zip',
  contentType: 'application/zip'
})

let putRequest = request.put({
  url: 'http://' + host + ':' + port + '/bootstrap',
  headers: form.getHeaders()
}, function (error, response, body) {
  if (error) throw error
  console.log(body)
})

form.pipe(putRequest)

【讨论】:

    猜你喜欢
    • 2016-09-17
    • 2021-12-27
    • 1970-01-01
    • 2018-06-04
    • 2016-03-17
    • 2013-10-02
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多