网上虽然有各种实例,可在实际使用中,存在各种问题,同时对错误并没有完善的处理。所以经过反复的调试,成功后,在此做个知识总结。
需求
- 开发语言: Typescript
- 在 Electron Render 线程中调用
- 用 Promise 方式实现
- 下载用 pipe 方式,更好的支持大文件
- 上传支持附加请求参数
- 上传支持附加头部信息
- 完善的错误捕获逻辑,抛出统一的错误类型,方便捕获处理
依赖库
# For download
yarn add request
# For upload
yarn add form-data
逻辑代码
transfer.ts
import FormData from 'form-data/lib/form_data';
import request from 'request';
import * as fs from 'fs';
import { IncomingMessage } from 'http';
export function TransferError(message: string = '', code: number = -1) {
this.name = 'TransferError';
this.message = message;
this.code = code;
}
TransferError.prototype = Error.prototype;
// Transfer file between local and remote.
class Transfer {
debug: Function | null = null; // Example: transfer.debug = console.log
// Upload file to remote.
upload(
url: string,
filePath: string,
params: any = {},
addHeaders: any = {}
) {
return new Promise((resolve, reject) => {
const stream = fs.createReadStream(filePath);
stream.on('error', (e: any) => reject(new TransferError(e)));
const form = new FormData();
form.append('file', stream);
for (const k in params) {
form.append(k, params[k]);
}
const u = new URL(url);
const data = {
protocol: u.protocol,
host: u.hostname,
port: u.port,
path: u.pathname,
headers: {},
};
for (const k in addHeaders) {
data.headers[k] = addHeaders[k];
}
if (this.debug) {
this.debug(`[transfer] upload - data: ${JSON.stringify(data)}`);
}
form
.submit(data, (e: any, resp: IncomingMessage) => {
if (e || resp.statusCode !== 200) {
if (resp) {
reject(new TransferError(resp.statusMessage, resp.statusCode));
} else {
reject(new TransferError(e));
}
} else {
resolve(resp.read());
}
})
.on('error', (e: any) => {
reject(new TransferError(e));
});
});
}
// Download file from remote.
download(url: string, destPath: string) {
return new Promise((resolve, reject) => {
const r = request(url);
r.on('response', (resp: IncomingMessage) => {
if (resp.statusCode === 200) {
const writer = fs.createWriteStream(destPath);
r.pipe(writer)
.on('error', (e: any) => reject(new TransferError(e)))
.on('finish', resolve);
} else {
reject(new TransferError(resp.statusMessage, resp.statusCode));
}
}).on('error', (e: any) => reject(new TransferError(e)));
});
}
}
export const transfer = new Transfer();
测试代码
transfer_test.ts
import { transfer, TransferError } from './transfer';
async function upload() {
const url = 'http://192.168.10.11:8081/upload';
const filePath = '/Users/mac/Desktop/upload.jpg';
const openid = 'cd2b61ce043f6eed3f9d86aa6e670dba91a436ac';
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyIjp7ImlkIjoxLCJvcGVuaWQiOiJlZjE2NDU3YS0wNDgxLTRlYzUtOTQzMS0yZDhlYTA3MDdkODkifSwiZXhwIjoxNTc2NTAwNTA4LCJpc3MiOiJub3RlZG93bi5hdXRoIn0.86IwjCon8F3s5Pngn6gK5uuPrxEUmgFvzpkm_Jfhrzc';
const addHeaders = {
Authorization: `token ${token}`,
};
transfer.debug = console.log;
try {
const resp = await transfer.upload(url, filePath, { openid }, addHeaders);
console.log('upload success', resp);
} catch (e) {
if (e instanceof TransferError) {
console.log(e);
} else {
console.error(e);
}
}
}
async function download() {
const url =
'http://192.168.10.11:8081/static/86500d81f4a0921e27b4fbd89ce1eb1cc164109f.jpg';
const filePath = '/Users/mac/Desktop/download.jpg';
try {
const resp = await transfer.download(url, filePath);
console.log('download success', resp);
} catch (e) {
if (e instanceof TransferError) {
console.log(e);
} else {
console.error(e);
}
}
}
export default function test() {
upload();
download();
}