@lakutata/core
Version:
Lakutata Framework Core
241 lines (232 loc) • 7.99 kB
text/typescript
import {Plugin} from '../base/Plugin'
import {
get as needleGet,
post as needlePost,
put as needlePut,
patch as needlePatch,
head as needleHead,
delete as needleDelete,
request as needleRequest, ReadableStream
} from 'needle'
import {URL} from 'url'
import {NeedleOptions as HttpRequestOptions} from 'needle'
import {HttpRequestException} from '../exceptions/HttpRequestException'
import fs from 'fs'
declare module '../Core' {
interface Application {
HttpRequest: HttpRequest
}
}
export class HttpRequest extends Plugin {
/**
* Multipart上传
* @param {{method?: "post" | "put", url: string | URL, filename?: string, file: string, contentType?: string, field?: string, options?: HttpRequestOptions}} setting
* @returns {Promise<T>}
*/
public async multipartUpload<T = any>(setting: {
method?: 'post' | 'put',
url: string | URL,
filename?: string,
file: string,
contentType?: string,
field?: string,
options?: HttpRequestOptions
}): Promise<T> {
return new Promise((resolve, reject) => {
const method: 'post' | 'put' = setting.method ? setting.method : 'put'
const url: string = setting.url.toString()
const filename: string | undefined = setting.filename
const file: string = setting.file
const contentType: string = setting.contentType ? setting.contentType : 'application/octet-stream'
const field: string = setting.field ? setting.field : 'file'
const uploadDataObject = {
[field]: {
filename: filename,
content_type: contentType,
file: file
}
}
if (!fs.existsSync(file)) return reject(new HttpRequestException([`File [${file}] does not exist`, method.toUpperCase(), url]))
const fileStat: fs.Stats = fs.statSync(file)
if (!fileStat.isFile()) return reject(new HttpRequestException([`[${file}] is not file`, method.toUpperCase(), url]))
needleRequest(method, url, uploadDataObject, Object.assign(setting.options ? setting.options : {}, {multipart: true}), (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, method.toUpperCase(), url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* 文件下载
* @param setting
* @param {(exception: (HttpRequestException | null), isDone: boolean, progress: number) => void} progressCallback
* @returns {Promise<void>}
*/
public async download(setting: {
method?: 'get' | 'post',
url: string | URL,
filename: string,
params?: { [key: string]: any } | string | null,
options?: HttpRequestOptions
},
progressCallback?: (exception: HttpRequestException | null, isDone: boolean, progress: number) => void): Promise<void> {
return new Promise((resolve, reject) => {
const method: 'get' | 'post' = setting.method ? setting.method : 'get'
const filename: string = setting.filename
const url: string = setting.url.toString()
const params: { [key: string]: any } | string | null = setting.params ? setting.params : null
const options: HttpRequestOptions = Object.assign(setting.options ? setting.options : {}, {output: filename})
const stream: ReadableStream = needleRequest(method, url, params, options)
let dataSize: number = 0
let receivedDataSize: number = 0
let progress: number = 0
const internalProgressCallback = (exception: HttpRequestException | null, isDone: boolean, progress: number) => {
if (progressCallback) progressCallback(exception, isDone, progress)
if (exception) {
reject(exception)
}
if (isDone) {
resolve()
}
}
stream
.on('header', (statusCode, headers) => {
dataSize = headers['content-length'] ? parseInt(headers['content-length']) : 0
internalProgressCallback(null, false, progress)
})
.on('readable', () => {
let chunk: Buffer | string
while (chunk = stream.read()) {
receivedDataSize += chunk.length
if (dataSize > 0) {
progress = (receivedDataSize / dataSize) * 100
} else {
progress = 0
}
fs.appendFileSync(filename, chunk)
internalProgressCallback(null, false, progress)
}
})
.on('done', (downloadError => {
let downloadException: HttpRequestException | null = null
if (downloadError) {
downloadException = new HttpRequestException([downloadError.message, method.toUpperCase(), url.toString()])
} else if (dataSize === 0) {
downloadException = new HttpRequestException(['Received 0 length data from server', method.toUpperCase(), url.toString()])
}
internalProgressCallback(downloadException, true, progress)
}))
.once('error', error => {
internalProgressCallback(new HttpRequestException([error.message, method.toUpperCase(), url.toString()]), false, progress)
})
})
}
/**
* Get请求
* @param {string | URL} url
* @param {HttpRequestOptions} options
* @returns {Promise<T>}
*/
public async get<T = any>(url: string | URL, options?: HttpRequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
needleGet(url.toString(), options, (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, 'GET', url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* Post请求
* @param {string | URL} url
* @param body
* @param {HttpRequestOptions} options
* @returns {Promise<T>}
*/
public async post<T = any>(url: string | URL, body: { [key: string]: any } | string | Buffer | null, options?: HttpRequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
needlePost(url.toString(), body, options, (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, 'POST', url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* Put请求
* @param {string | URL} url
* @param body
* @param {HttpRequestOptions} options
* @returns {Promise<T>}
*/
public async put<T = any>(url: string | URL, body: { [key: string]: any } | string | Buffer | null, options?: HttpRequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
needlePut(url.toString(), body, options, (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, 'PUT', url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* Patch请求
* @param {string | URL} url
* @param body
* @param {HttpRequestOptions} options
* @returns {Promise<T>}
*/
public async patch<T = any>(url: string | URL, body: { [key: string]: any } | string | Buffer | null, options?: HttpRequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
needlePatch(url.toString(), body, options, (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, 'PATCH', url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* Delete请求
* @param {string | URL} url
* @param body
* @param {HttpRequestOptions} options
* @returns {Promise<T>}
*/
public async delete<T = any>(url: string | URL, body: { [key: string]: any } | string | Buffer | null, options?: HttpRequestOptions): Promise<T> {
return new Promise((resolve, reject) => {
needleDelete(url.toString(), body, options, (error, response) => {
if (error) {
reject(new HttpRequestException([error.message, 'DELETE', url.toString()]))
} else {
resolve(response.body)
}
})
})
}
/**
* Head请求
* @param {string | URL} url
* @param {HttpRequestOptions} options
* @returns {Promise<boolean>}
*/
public async head(url: string | URL, options?: HttpRequestOptions): Promise<boolean> {
return new Promise((resolve) => {
needleHead(url.toString(), options, (error) => {
if (error) {
resolve(false)
} else {
resolve(true)
}
})
})
}
}