NodeJS:将本地文件发送/上传到远程服务器

23

我使用Winston模块为我的离线应用程序创建了一个每日日志文件。现在我需要能够通过POST将该文件发送或上传到远程服务器(这部分已经存在)。

我知道我需要分块写入文件,以避免占用内存,因此我正在使用fs.createReadStream,但是即使只发送示例文本,我似乎仍然只得到503响应。

编辑

我发现接收方希望数据被命名为“data”。我已经删除了createReadSteam,因为我只能使用“application/x-www-form-urlencoded”和同步的fs.readFileSync让它工作。如果我在php服务器上将其更改为“multipart/form-data”,那么我是否可以再次使用createReadStream,还是只有当我更改为物理上传JSON文件时才能使用?

我只学习了几个星期的node,所以非常感谢任何指导。

var http = require('http'),
    fs = require('fs');

var post_options = {
    host: 'logger.mysite.co.uk',
    path: '/',
    port: 80,
    timeout: 120000,
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
}

var sender = http.request(post_options, function(res) {
    if (res.statusCode < 399) {
        var text = ""
        res.on('data', function(chunk) {
            text += chunk
        })
        res.on('end', function(data) {
            console.log(text)
        })
    } else {
        console.log("ERROR", res.statusCode)
    }
})

var POST_DATA = 'data={['
POST_DATA += fs.readFileSync('./path/file.log').toString().replace(/\,+$/,'')
POST_DATA += ']}'
console.log(POST_DATA)
sender.write(POST_DATA)
sender.end()

1
你尝试使用类似curl的工具上传文件,以确保远程服务器实际接受数据了吗?它可能会有application/json内容类型的问题。另外,日志文件是否真的是JSON格式的? - robertklep
你说得有道理。上传 JSON 文件和打开 JSON 文件并将其作为 POST 数据块发送(实际上不会发送文件),是否有区别? - sidonaldson
如果您使用Content-Typeapplication/json发送它,则该文件可能不会被视为“常规”上传。 如果您想进行常规上传,您应该考虑使用request或至少form-data,因为文件上传不是易于实现的。 - robertklep
这是服务器实际期望的吗?看起来相当奇怪。 - robertklep
503是服务器暂时无法使用-服务器宕机了吗?http://www.checkupdown.com/status/E503.html - bryanmac
显示剩余3条评论
4个回答

11

经过无数次尝试失败后,我终于找到了解决方法。使用node-fetchFormData。顺便提一下,request已于两天前被弃用。

const FormData = require('form-data');
const fetch = require('node-fetch');

function uploadImage(imageBuffer) {
  const form = new FormData();
  form.append('file', imageBuffer, {
    contentType: 'image/jpeg',
    filename: 'dummy.jpg',
  });
  return fetch(`myserver.cz/upload`, { method: 'POST', body: form })
};

imageBuffer的位置,可能有很多东西。我有一个包含图像的缓冲区,但您也可以传递fs.createReadStream('/ foo / bar.jpg')的结果来上传驱动器中的文件。


1
我已经为此苦苦挣扎了好几天,而这个答案正是我所需要的。 - Stewart Knapman
imageBuffer是什么?是文件路径吗? - Nikhil Patil
1
@NikhilPatil 不对。Node.js 有一个叫做 Buffer 的实际东西来存储字节数据。我在答案中进行了更详细的阐述。但你可以直接查看文档。链接就在那里,不是吗? - kub1x
1
完美的答案,而且库的大小也非常小。 - soundly_typed

9

复制自https://github.com/mikeal/request#forms

表单数据可以通过两种不同的方式发送:作为表单编码(application/x-www-form-urlencoded)或多部分(multipart/form-data)。这个模块默认使用表单编码,但如果您需要上传文件,则必须显式地设置请求主体以使用多部分。
var r = request.post('http://service.com/upload', function optionalCallback (err, httpResponse, body) {
  if (err) {
    return console.error('upload failed:', err);
  }
  console.log('Upload successful!  Server responded with:', body);
})
var form = r.form()
form.append('my_field1', 'my_value23_321')
form.append('my_field2', '123123sdas')
form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png')))

1
请求已过时,请查看我的答案,使用 node-fetchFormData:https://dev59.com/UWIj5IYBdhLWcg3w24kF#60208227。 - kub1x

0
我建议使用axios将其上传到远程服务器。
const { default: axios } = require("axios")
const fs = require('fs');

const path = '/path/to/your/file.{extension}'
const file = fs.readFileSync(path) /** Read file */
const stats = fs.statSync(path) /** Get file size in bytes (for content-length) */
const fileSizeInBytes = stats.size;

/** Add appropriate headers */
const headers = {
  'Authorization': 'Bearer Your Token', /** Optional */
  'Content-Length': fileSizeInBytes, /** Recommended to add it */
  'Content-Type': 'application/octet-stream',
}
let url = "https://www.example.com/remote-server-upload-url"

axios.post(url, file, {
    headers: headers,
    maxContentLength: Infinity, /** To avoid max content length error */
    maxBodyLength: Infinity /** To avoid max body length error */
}).then((response) => {
    return response.data
}).catch((error) => {
    return error
})

-1

看一下 request 模块。

它可以让你将文件流式传输到 POST 请求中。


我明白了。请求允许您直接将文件流传输到另一个请求。请查看文档。 - swhitewvu24
从文档中:fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json')) - swhitewvu24
我同意request非常好用,但由于上传的原因,远程服务器出现了问题,所以我认为用request替换http并不能解决这个问题 :) 原始代码虽然有点啰嗦,但应该可以正常工作。 - robertklep
此外,我正在尝试在可能的情况下学习Node。我之前看过request,但由于这很复杂,我可能不得不再次查看它。 - sidonaldson
2
@sidonaldson 使用其他人制作的优秀模块也是学习 Node 的一部分 ;) - robertklep
1
@robertklep 当我第一次开始学习Node.js的时候,我发现request模块太过令人困惑了,好像它做了太多事情,太快了。现在我知道如果我回去重新开始,它会更加容易理解。 - sidonaldson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接