原文鏈接 https://www.yuque.com/egg/nodejs/httpclient-upload
背景
互聯網時代,無數服務是基於 HTTP 協議進行通信的。
除了常見的 前端瀏覽器 -> Node 應用
外, Node 應用 -> 后端服務
也是一種非常常見的應用場景。
譬如:
- 調用后端微服務,查詢或更新數據。
- 把日志上報給第三方服務。
- 發送文件給后端服務。
Node.js 本身有提供了 http.request() 的能力,但它太底層了,因此社區有 request、superagent 等庫。
我們在日常工作中也沉淀出了 urllib 這個基礎庫,可以使用它來非常便捷地完成任何 HTTP 請求。
request
目前已經放棄維護,詳見 GitHub 置頂的 2 個 issue:
回到場景,『發送文件給后端服務』是其中一個非常典型的場景,對應於 RFC 1867 規范。
POST https://httpbin.org/post HTTP/1.1
Host: httpbin.org
Content-Length: 495
Content-Type: multipart/form-data; boundary=---------------------------7db2d1bcc50e6e
-----------------------------7db2d1bcc50e6e
Content-Disposition: form-data; name="foo"
bar
-----------------------------7db2d1bcc50e6e
Content-Disposition: form-data; name="upload1"; filename="/tmp/file.md"
Content-Type: text/plain
This is file1.
-----------------------------7db2d1bcc50e6e
Content-Disposition: form-data; name="upload2"; filename="/tmp/file2.md"
Content-Type: text/plain
This is file2, it's longer.
-----------------------------7db2d1bcc50e6e--
然而,對於前端新手來說,有一定的學習門檻,因此,我們提供了一種簡化的方式,來減輕新手上手成本。
舊模式
需要自行引入 formstream 這個模塊來來幫助我們生成可以被 HttpClient 消費的 form 對象。
const FormStream = require('formstream');
const httpclient = require('urllib');
async function run() {
// 構造對應的 form stream
const form = new FormStream();
form.field('foo', 'bar'); // 設置普通的 headers
form.file('file', __filename); // 添加文件,上傳當前文件本身用於測試
// form.file('file2', __filename); // 執行多次來添加多文件
// 發起請求
const url = 'https://httpbin.org/post';
const result = await httpclient.request(url, {
dataType: 'json',
method: 'POST',
// 生成符合 multipart/form-data 要求的請求 headers
headers: form.headers(),
// 以 stream 模式提交
stream: form,
});
console.log(result.data.files);
// 響應最終會是類似以下的結果:
// {
// "file": "'use strict';\n\nconst For...."
// }
}
run().catch(console.error);
新模式
開發者無需自行組裝和引入額外模塊,僅需提供 files
這個參數即可。
const httpclient = require('urllib');
async function run() {
// 發起請求
const url = 'https://httpbin.org/post';
const result = await httpclient.request(url, {
dataType: 'json',
method: 'POST',
// 設置普通的 headers
data: {
foo: 'bar',
},
// 單文件上傳
files: __filename,
// 多文件上傳
// files: {
// file1: __filename,
// file2: fs.createReadStream(__filename),
// file3: Buffer.from('mock file content'),
// },
});
console.log(result.data.files);
}
run().catch(console.error);
在 Egg 中使用
Egg 基於 urllib 內置實現了一個 HttpClient,方便應用開發者便捷地發起 HTTP 請求。
注意:本文介紹的是
Node 應用 -> 后端服務
之間的文件上傳。如果你想了解的是
前端瀏覽器 -> Node 應用
之間的文件上傳,請參考對應的文檔。
// app/controller/http.js
class HttpController extends Controller {
async upload() {
const { ctx } = this;
const result = await ctx.curl('https://httpbin.org/post', {
method: 'POST',
dataType: 'json',
// 設置普通的 headers
data: {
foo: 'bar',
},
// 單文件上傳
files: __filename,
// 多文件上傳
// files: {
// file1: __filename,
// file2: fs.createReadStream(__filename),
// file3: Buffer.from('mock file content'),
// },
});
ctx.body = result.data.files;
// 響應最終會是類似以下的結果:
// {
// "file": "'use strict';\n\nconst For...."
// }
}
}
相關資料
- 本文介紹的是
Node 應用 -> 后端服務
之間的文件上傳,如果你想了解的是前端瀏覽器 -> Node 應用
之間的文件上傳,請參考對應的文檔。 - 相關文檔:httpclient 。
- 對應的 PR 實現:https://github.com/node-modules/urllib/pull/322