file-box
Version:
Pack a File into Box for easy move/transfer between servers no matter of where it is.(local path, remote url, or cloud storage)
143 lines (114 loc) • 3.4 kB
text/typescript
import http from 'http'
import https from 'https'
import nodeUrl from 'url'
import type stream from 'stream'
export function dataUrlToBase64 (dataUrl: string): string {
const dataList = dataUrl.split(',')
return dataList[dataList.length - 1]!
}
/**
* Get http headers for specific `url`
* follow 302 redirection for max `REDIRECT_TTL` times.
*
* @credit https://stackoverflow.com/a/43632171/1123955
*/
export async function httpHeadHeader (url: string): Promise<http.IncomingHttpHeaders> {
let REDIRECT_TTL = 7
while (true) {
if (REDIRECT_TTL-- <= 0) {
throw new Error(`ttl expired! too many(>${REDIRECT_TTL}) 302 redirection.`)
}
const res = await _headHeader(url)
if (!/^3/.test(String(res.statusCode))) {
return res.headers
}
// console.log('302 found for ' + url)
if (!res.headers.location) {
throw new Error('302 found but no location!')
}
url = res.headers.location
}
async function _headHeader (destUrl: string): Promise<http.IncomingMessage> {
const parsedUrl = nodeUrl.parse(destUrl)
const options = {
...parsedUrl,
method : 'HEAD',
// method : 'GET',
}
let request: typeof http.request
if (parsedUrl.protocol === 'https:') {
request = https.request
} else if (parsedUrl.protocol === 'http:') {
request = http.request
} else {
throw new Error('unknown protocol: ' + parsedUrl.protocol)
}
return new Promise<http.IncomingMessage>((resolve, reject) => {
request(options, resolve)
.on('error', reject)
.end()
})
}
}
export function httpHeaderToFileName (
headers: http.IncomingHttpHeaders,
): null | string {
const contentDisposition = headers['content-disposition']
if (!contentDisposition) {
return null
}
// 'content-disposition': 'attachment; filename=db-0.0.19.zip'
const matches = contentDisposition.match(/attachment; filename="?(.+[^"])"?$/i)
if (matches && matches[1]) {
return matches[1]
}
return null
}
export async function httpStream (
url : string,
headers : http.OutgoingHttpHeaders = {},
): Promise<http.IncomingMessage> {
/* eslint node/no-deprecated-api: off */
// FIXME:
const parsedUrl = nodeUrl.parse(url)
const protocol = parsedUrl.protocol
let options: http.RequestOptions
let get: typeof https.get
if (!protocol) {
throw new Error('protocol is empty')
}
if (protocol.match(/^https:/i)) {
get = https.get
options = parsedUrl
options.agent = https.globalAgent
} else if (protocol.match(/^http:/i)) {
get = http.get
options = parsedUrl
options.agent = http.globalAgent
} else {
throw new Error('protocol unknown: ' + protocol)
}
options.headers = {
...options.headers,
...headers,
}
const res = await new Promise<http.IncomingMessage>((resolve, reject) => {
get(options, resolve)
.on('error', reject)
.end()
})
return res
}
export async function streamToBuffer (
stream: stream.Readable,
): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => {
const bufferList: Buffer[] = []
stream.once('error', reject)
stream.once('end', () => {
const fullBuffer = Buffer.concat(bufferList)
resolve(fullBuffer)
})
stream.on('data', buffer => bufferList.push(buffer))
})
}