Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/commons/downloadFile.ts
1028 views
1
import * as http from 'http';
2
import { HttpsProxyAgent } from 'https-proxy-agent';
3
import * as https from 'https';
4
import { RequestOptions } from 'https';
5
import { parse } from 'url';
6
import { createWriteStream } from 'fs';
7
import { createPromise } from './utils';
8
import { getProxyForUrl } from './getProxyForUrl';
9
10
export default function downloadFile(
11
url: string,
12
destinationPath: string,
13
progressCallback?: (downloadedBytes: number, totalBytes: number) => void,
14
): Promise<void> {
15
const downloaderPromise = createPromise<void>();
16
let downloadedBytes = 0;
17
let totalBytes = 0;
18
19
const request = httpGet(url, response => {
20
if (response.statusCode !== 200) {
21
const error = new Error(
22
`Download failed: server returned code ${response.statusCode}. URL: ${url}`,
23
);
24
// consume response data to free up memory
25
response.resume();
26
downloaderPromise.reject(error);
27
return;
28
}
29
const file = createWriteStream(destinationPath);
30
file.once('finish', downloaderPromise.resolve);
31
file.once('error', downloaderPromise.reject);
32
response.pipe(file);
33
totalBytes = parseInt(response.headers['content-length'], 10);
34
if (progressCallback) response.on('data', onData);
35
});
36
request.once('error', downloaderPromise.reject);
37
return downloaderPromise.promise;
38
39
function onData(chunk: Buffer | string): void {
40
downloadedBytes += Buffer.byteLength(chunk);
41
progressCallback(downloadedBytes, totalBytes);
42
}
43
}
44
45
export function httpGet(
46
url: string,
47
response: (x: http.IncomingMessage) => void,
48
): http.ClientRequest {
49
const options = getRequestOptionsWithProxy(url);
50
const httpModule = options.protocol === 'https:' ? https : http;
51
52
const request = httpModule.request(options, (res: http.IncomingMessage): void => {
53
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
54
httpGet(res.headers.location, response);
55
} else {
56
response(res);
57
}
58
});
59
request.end();
60
return request;
61
}
62
63
function getRequestOptionsWithProxy(url: string): RequestOptions {
64
const urlParsed = parse(url);
65
66
const options: https.RequestOptions = {
67
...urlParsed,
68
method: 'GET',
69
};
70
71
const proxyURL = getProxyForUrl(url);
72
if (proxyURL) {
73
if (url.startsWith('http:')) {
74
return {
75
path: urlParsed.href,
76
host: proxyURL.hostname,
77
port: proxyURL.port,
78
};
79
}
80
options.agent = new HttpsProxyAgent(proxyURL.href);
81
options.rejectUnauthorized = false;
82
}
83
return options;
84
}
85
86