Skip to content

Commit 0e3441d

Browse files
committed
improved progress reporting and migrated upload from node-fetch to axios
1 parent 992d76b commit 0e3441d

8 files changed

Lines changed: 44 additions & 84 deletions

File tree

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
"express-basic-auth": "1.2.1",
5555
"fast-xml-parser": "4.5.1",
5656
"mime-types": "2.1.35",
57-
"node-fetch": "2.7.0",
5857
"openpgp": "5.11.2",
5958
"pm2": "5.4.3",
6059
"range-parser": "^1.2.1",
@@ -75,8 +74,7 @@
7574
"@types/express": "5.0.0",
7675
"@types/mime-types": "2.1.4",
7776
"@types/node": "22.10.2",
78-
"@types/node-fetch": "2.6.12",
79-
"@types/range-parser": "^1.2.7",
77+
"@types/range-parser": "1.2.7",
8078
"@vitest/coverage-istanbul": "2.1.8",
8179
"@vitest/spy": "2.1.8",
8280
"eslint": "9.17.0",

src/commands/download-file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,13 @@ export default class DownloadFile extends Command {
7979
user.bucket,
8080
user.mnemonic,
8181
driveFile.fileId,
82+
driveFile.size,
8283
StreamUtils.writeStreamToWritableStream(fileWriteStream),
8384
undefined,
8485
{
8586
abortController: new AbortController(),
8687
progressCallback: (progress) => {
87-
progressBar.update(progress);
88+
progressBar.update(progress * 0.99);
8889
},
8990
},
9091
);

src/commands/upload-file.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,22 @@ export default class UploadFile extends Command {
7474

7575
CLIUtils.done();
7676

77-
const timer = CLIUtils.timer();
7877
// 2. Upload file to the Network
7978
const fileStream = createReadStream(filePath);
79+
const timer = CLIUtils.timer();
8080
const progressBar = CLIUtils.progress({
8181
format: 'Uploading file [{bar}] {percentage}%',
8282
linewrap: true,
8383
});
84-
progressBar.start(1, 0);
84+
progressBar.start(100, 0);
8585
const [uploadPromise, abortable] = await networkFacade.uploadFromStream(
8686
user.bucket,
8787
user.mnemonic,
8888
stats.size,
8989
fileStream,
9090
{
9191
progressCallback: (progress) => {
92-
progressBar.update(progress);
92+
progressBar.update(progress * 0.99);
9393
},
9494
},
9595
);
@@ -100,7 +100,6 @@ export default class UploadFile extends Command {
100100
});
101101

102102
const uploadResult = await uploadPromise;
103-
progressBar.stop();
104103

105104
// 3. Create the file in Drive
106105
const fileInfo = path.parse(filePath);
@@ -115,6 +114,9 @@ export default class UploadFile extends Command {
115114
name: '',
116115
});
117116

117+
progressBar.update(100);
118+
progressBar.stop();
119+
118120
const uploadTime = timer.stop();
119121
this.log('\n');
120122
// eslint-disable-next-line max-len

src/services/network/download.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export class DownloadService {
55

66
async downloadFile(
77
url: string,
8+
size: number,
89
options: {
910
progressCallback?: (progress: number) => void;
1011
abortController?: AbortController;
@@ -14,9 +15,8 @@ export class DownloadService {
1415
const response = await axios.get(url, {
1516
responseType: 'stream',
1617
onDownloadProgress(progressEvent) {
17-
if (options.progressCallback && progressEvent.total) {
18-
const reportedProgress = progressEvent.loaded / progressEvent.total;
19-
18+
if (options.progressCallback && progressEvent.loaded) {
19+
const reportedProgress = Math.round((progressEvent.loaded / size) * 100);
2020
options.progressCallback(reportedProgress);
2121
}
2222
},

src/services/network/network-facade.service.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { UploadService } from './upload.service';
1616
import { DownloadService } from './download.service';
1717
import { ValidationService } from '../validation.service';
1818
import { HashStream } from '../../utils/hash.utils';
19-
import { ProgressTransform } from '../../utils/stream.utils';
2019
import { RangeOptions } from '../../utils/network.utils';
2120

2221
export class NetworkFacade {
@@ -54,6 +53,7 @@ export class NetworkFacade {
5453
bucketId: string,
5554
mnemonic: string,
5655
fileId: string,
56+
size: number,
5757
to: WritableStream,
5858
rangeOptions?: RangeOptions,
5959
options?: DownloadOptions,
@@ -67,10 +67,6 @@ export class NetworkFacade {
6767
options.progressCallback(progress);
6868
};
6969

70-
const onDownloadProgress = (progress: number) => {
71-
onProgress(progress);
72-
};
73-
7470
const decryptFile: DecryptFileFunction = async (_, key, iv) => {
7571
let startOffsetByte;
7672
if (rangeOptions) {
@@ -96,8 +92,8 @@ export class NetworkFacade {
9692
throw new Error('Download aborted');
9793
}
9894

99-
const encryptedContentStream = await this.downloadService.downloadFile(downloadable.url, {
100-
progressCallback: onDownloadProgress,
95+
const encryptedContentStream = await this.downloadService.downloadFile(downloadable.url, size, {
96+
progressCallback: onProgress,
10197
abortController: options?.abortController,
10298
rangeHeader: rangeOptions?.range,
10399
});
@@ -142,11 +138,7 @@ export class NetworkFacade {
142138
const hashStream = new HashStream();
143139
const abortable = options?.abortController ?? new AbortController();
144140
let encryptionTransform: Transform;
145-
const progressTransform = new ProgressTransform({ totalBytes: size }, (progress) => {
146-
if (options?.progressCallback) {
147-
options.progressCallback(progress * 0.95);
148-
}
149-
});
141+
let hash: Buffer;
150142

151143
const onProgress: UploadProgressCallback = (progress: number) => {
152144
if (!options?.progressCallback) return;
@@ -165,15 +157,14 @@ export class NetworkFacade {
165157
};
166158

167159
const uploadFile: UploadFileFunction = async (url) => {
168-
await this.uploadService.uploadFile(url, encryptionTransform.pipe(progressTransform), {
160+
await this.uploadService.uploadFile(url, size, encryptionTransform, {
169161
abortController: abortable,
170-
progressCallback: () => {
171-
// No progress here, we are using the progressTransform
172-
},
162+
progressCallback: onProgress,
173163
});
174-
175-
return hashStream.getHash().toString('hex');
164+
hash = hashStream.getHash();
165+
return hash.toString('hex');
176166
};
167+
177168
const uploadOperation = async () => {
178169
const uploadResult = await NetworkUpload.uploadFile(
179170
this.network,
@@ -184,12 +175,10 @@ export class NetworkFacade {
184175
encryptFile,
185176
uploadFile,
186177
);
187-
const fileHash: Buffer = Buffer.from('');
188178

189-
onProgress(1);
190179
return {
191180
fileId: uploadResult,
192-
hash: fileHash,
181+
hash: hash,
193182
};
194183
};
195184

src/services/network/upload.service.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { Readable } from 'node:stream';
2-
import fetch from 'node-fetch';
3-
import { AbortSignal } from 'node-fetch/externals';
2+
import axios from 'axios';
43
import { UploadOptions } from '../../types/network.types';
54

65
export class UploadService {
76
public static readonly instance: UploadService = new UploadService();
87

9-
async uploadFile(url: string, from: Readable, options: UploadOptions): Promise<{ etag: string }> {
10-
const response = await fetch(url, {
11-
method: 'PUT',
12-
body: from,
13-
signal: options.abortController?.signal as AbortSignal,
8+
async uploadFile(url: string, size: number, from: Readable, options: UploadOptions): Promise<{ etag: string }> {
9+
const response = await axios.put(url, from, {
10+
signal: options.abortController?.signal,
11+
onUploadProgress: (progressEvent) => {
12+
if (options.progressCallback && progressEvent.loaded) {
13+
const reportedProgress = Math.round((progressEvent.loaded / size) * 100);
14+
options.progressCallback(reportedProgress);
15+
}
16+
},
1417
});
1518

16-
const etag = response.headers.get('etag');
17-
options.progressCallback(1);
19+
const etag = response.headers['etag'];
1820
if (!etag) {
1921
throw new Error('Missing Etag in response when uploading file');
2022
}

src/webdav/handlers/GET.handler.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,6 @@ export class GETRequestHandler implements WebDavMethodHandler {
4343
const { user } = await authService.getAuthDetails();
4444
webdavLogger.info(`[GET] [${driveFile.uuid}] Network ready for download`);
4545

46-
const writable = new WritableStream({
47-
write(chunk) {
48-
res.write(chunk);
49-
},
50-
close() {
51-
res.end();
52-
},
53-
});
54-
5546
const range = req.headers['range'];
5647
const rangeOptions = NetworkUtils.parseRangeHeader({
5748
range,
@@ -66,10 +57,20 @@ export class GETRequestHandler implements WebDavMethodHandler {
6657
res.header('Content-Type', 'application/octet-stream');
6758
res.header('Content-length', contentLength.toString());
6859

60+
const writable = new WritableStream({
61+
write(chunk) {
62+
res.write(chunk);
63+
},
64+
close() {
65+
res.end();
66+
},
67+
});
68+
6969
const [executeDownload] = await networkFacade.downloadToStream(
7070
driveFile.bucket,
7171
user.mnemonic,
7272
driveFile.fileId,
73+
contentLength,
7374
writable,
7475
rangeOptions,
7576
);

yarn.lock

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2937,14 +2937,6 @@
29372937
dependencies:
29382938
"@types/node" "*"
29392939

2940-
"@types/node-fetch@2.6.12":
2941-
version "2.6.12"
2942-
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03"
2943-
integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==
2944-
dependencies:
2945-
"@types/node" "*"
2946-
form-data "^4.0.0"
2947-
29482940
"@types/node-forge@^1.3.0":
29492941
version "1.3.11"
29502942
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da"
@@ -2971,7 +2963,7 @@
29712963
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a"
29722964
integrity sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==
29732965

2974-
"@types/range-parser@*", "@types/range-parser@^1.2.7":
2966+
"@types/range-parser@*", "@types/range-parser@1.2.7":
29752967
version "1.2.7"
29762968
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
29772969
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
@@ -6540,13 +6532,6 @@ node-addon-api@^7.0.0:
65406532
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
65416533
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
65426534

6543-
node-fetch@2.7.0:
6544-
version "2.7.0"
6545-
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
6546-
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
6547-
dependencies:
6548-
whatwg-url "^5.0.0"
6549-
65506535
node-forge@^1:
65516536
version "1.3.1"
65526537
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
@@ -8143,11 +8128,6 @@ touch@^3.1.0:
81438128
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694"
81448129
integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==
81458130

8146-
tr46@~0.0.3:
8147-
version "0.0.3"
8148-
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
8149-
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
8150-
81518131
triple-beam@^1.3.0:
81528132
version "1.4.1"
81538133
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984"
@@ -8530,19 +8510,6 @@ web-streams-polyfill@~3.0.3:
85308510
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz#f49e487eedeca47a207c1aee41ee5578f884b42f"
85318511
integrity sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==
85328512

8533-
webidl-conversions@^3.0.0:
8534-
version "3.0.1"
8535-
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
8536-
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
8537-
8538-
whatwg-url@^5.0.0:
8539-
version "5.0.0"
8540-
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
8541-
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
8542-
dependencies:
8543-
tr46 "~0.0.3"
8544-
webidl-conversions "^3.0.0"
8545-
85468513
which-boxed-primitive@^1.0.2:
85478514
version "1.0.2"
85488515
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"

0 commit comments

Comments
 (0)