@leogps/file-uploader 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,13 @@ Zero-config command-line tool to run a file-uploader server. Files can be upload
4
4
 
5
5
  Both Server and Client are written in JS.
6
6
 
7
+ ## Features
8
+ - Chunked/Resumable uploads
9
+ - Chunk size is configurable `-s | --chunk-size`
10
+ - Number of Parallel uploads are configurable `-n | --parallel-uploads`
11
+ - Uses SHA1 verification to ensure chunks are valid
12
+ - Optionally disable resumable uploads
13
+
7
14
  ## Installation
8
15
 
9
16
  #### Running on-demand:
@@ -40,11 +47,15 @@ with the provided Dockerfile.
40
47
 
41
48
  file-uploader [path] [options]
42
49
 
43
- --version Show version number [boolean]
44
- -l, --upload_location upload location
45
- [string] [default: "/Users/username/Downloads/uploads/"]
46
- -p, --port server port [number]
47
- --help Show help [boolean]
50
+ Options:
51
+ -l, --upload-location upload location [string] [default: "/Users/<username>/uploads/"]
52
+ -p, --port server port [number] [default: 8082]
53
+ -s, --chunk-size chunk size in bytes [number] [default: 524288]
54
+ -n, --parallel-uploads number of simultaneous parallel chunk uploads (per file) [number] [default: 10]
55
+ -c, --enable-compression enable gzip compression (server to client responses) [boolean] [default: true]
56
+ -m, --max-file-size maximum file size in bytes [number] [default: 107374182400]
57
+ --version Show version number [boolean]
58
+ --help Show help [boolean]
48
59
 
49
60
  # Development
50
61
 
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"><title>File Uploader!</title><link rel="icon" href="favicon.ico"><script defer="defer" src="main.66a16cbe5e2ce036e9a7.bundle.js"></script><link href="main.6db272040eaab1c51019.css" rel="stylesheet"></head><body><div class="content m-2"><div class="fixed-grid container has-4-columns mt-4"><div class="grid"><div class="cell is-col-span-2"><section><div>Hello there from file-uploader server.</div><div><code class="">File Uploader,</code> you know for file uploads.</div></section></div><div class="cell is-col-from-end-1"><section class="mt-4 m-2 is-pulled-right mr-6"><div class="is-position-absolute bulma-is-fixed-top is-clickable" id="themeToggle"><i class="fas fa-moon" id="themeIcon"></i></div></section></div></div></div><div class="container is-one-third"><form id="uploadForm" action="/upload" enctype="multipart/form-data" method="post"><div id="file-div" class="field file has-name is-boxed column is-flex-grow-1"><label class="file-label"><input class="file-input" type="file" name="multipleFiles" multiple="multiple"> <span class="file-cta"><span class="file-icon"><i class="fas fa-upload"></i> </span><span class="file-label">Choose file(s)…</span></span></label><div id="file-name" class="mt-1 wrap-text is-multiline"></div></div><div class="control field is-flex-grow-1"><button type="submit" class="button is-link">Submit</button></div></form></div><hr class="is-one-third"/><div class="container"><section class="m-2 is-one-third"><h4>Progress:</h4><div class="all-progress-detail-control control field is-flex-grow-1 is-active p-1 is-hidden"><button type="button" class="button is-link">Collapse All</button></div><div class="container" id="progress-container"></div></section></div></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"><title>File Uploader!</title><link rel="icon" href="favicon.ico"><script defer="defer" src="main.cb411c3ff6063891b944.bundle.js"></script><link href="main.6db272040eaab1c51019.css" rel="stylesheet"></head><body><div class="content m-2"><div class="fixed-grid container has-4-columns mt-4"><div class="grid"><div class="cell is-col-span-2"><section><div>Hello there from file-uploader server.</div><div><code class="">File Uploader,</code> you know for file uploads.</div></section></div><div class="cell is-col-from-end-1"><section class="mt-4 m-2 is-pulled-right mr-6"><div class="is-position-absolute bulma-is-fixed-top is-clickable" id="themeToggle"><i class="fas fa-moon" id="themeIcon"></i></div></section></div></div></div><div class="container is-one-third"><form id="uploadForm" action="/upload" enctype="multipart/form-data" method="post"><div id="file-div" class="field file has-name is-boxed column is-flex-grow-1"><label class="file-label"><input class="file-input" type="file" name="file" multiple="multiple"> <span class="file-cta"><span class="file-icon"><i class="fas fa-upload"></i> </span><span class="file-label">Choose file(s)…</span></span></label><div id="file-name" class="mt-1 wrap-text is-multiline"></div></div><div class="field"><label class="checkbox"><input id="disableChunkedUpload" type="checkbox"> <span>Disable chunked/resumable upload</span></label></div><div class="control field is-flex-grow-1"><button type="submit" class="button is-link">Submit</button></div></form></div><hr class="is-one-third"/><div class="container"><section class="m-2 is-one-third"><h4>Progress:</h4><div class="all-progress-detail-control control field is-flex-grow-1 is-active p-1 is-hidden"><button type="button" class="button is-link">Collapse All</button></div><div class="container" id="progress-container"></div></section></div></div></body></html>
@@ -6250,12 +6250,13 @@ class ProgressHandler {
6250
6250
  <i class="fas fa-minus-circle m-0 p-0" aria-hidden="true" title="collapse"></i>
6251
6251
  </span>
6252
6252
  </a>
6253
- <span class="ml-0 pl-0">
6253
+ <span class="upload-file-name ml-0 pl-0">
6254
6254
  ${progress.fileName}
6255
6255
  </span>
6256
6256
  </div>`);
6257
6257
  $panel.append($panelHeading);
6258
6258
  }
6259
+ $panelHeading.find(".upload-file-name").text(progress.fileName || "");
6259
6260
  // Main progress bar (bytes)
6260
6261
  let $progressElem = $panel.find(`progress#${progressId}`);
6261
6262
  if (!$progressElem.length) {
@@ -6280,6 +6281,10 @@ class ProgressHandler {
6280
6281
  }
6281
6282
  // Clear previous rows
6282
6283
  $table.empty();
6284
+ let progressPercent = 0;
6285
+ if (progress.bytesReceived !== undefined && progress.bytesExpected !== undefined) {
6286
+ progressPercent = (progress.bytesReceived / progress.bytesExpected) * 100;
6287
+ }
6283
6288
  // Define table rows
6284
6289
  const rows = [
6285
6290
  // ["File Name", progress.fileName || "-"],
@@ -6291,6 +6296,7 @@ class ProgressHandler {
6291
6296
  <b>|</b> Uploaded: ${uploaded}/${totalChunks}`],
6292
6297
  ["Speed", `${(0, pretty_bytes_1.default)(progress_utils_1.ProgressUtils.calculateTransferRate(progress))}/s`],
6293
6298
  ["Status", `${progress.lastState || "-"}`],
6299
+ ["Progress", `${progressPercent}%`],
6294
6300
  ];
6295
6301
  if (progress.completed) {
6296
6302
  const timeTaken = ((progress.completed - (progress.timestamp || 0)) / 1000).toFixed(2);
@@ -21014,15 +21020,32 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
21014
21020
  exports.ProgressUtils = void 0;
21015
21021
  class ProgressUtils {
21016
21022
  static calculateTransferRate(progress) {
21017
- const transferSamples = progress.transferSamples;
21018
- if (!transferSamples || transferSamples.length < 2) {
21023
+ const samples = progress.transferSamples;
21024
+ if (!samples || samples.length < 2) {
21025
+ return 0;
21026
+ }
21027
+ let totalBytes = 0;
21028
+ let totalTimeMs = 0;
21029
+ // Case vs Reason for skipping
21030
+ // bytes === 0 idle / waiting / verification
21031
+ // bytes < 0 possibly corrupted or reset counter
21032
+ // timeMs === 0 divide-by-zero risk
21033
+ // timeMs < 0 invalid timestamp
21034
+ // To prevent idle gaps from dragging the rate down artificially.
21035
+ for (let i = 1; i < samples.length; i++) {
21036
+ const prev = samples[i - 1];
21037
+ const curr = samples[i];
21038
+ const bytes = curr.bytesReceived - prev.bytesReceived;
21039
+ const timeMs = curr.timestamp - prev.timestamp;
21040
+ if (bytes > 0 && timeMs > 0) {
21041
+ totalBytes += bytes;
21042
+ totalTimeMs += timeMs;
21043
+ }
21044
+ }
21045
+ if (totalTimeMs === 0) {
21019
21046
  return 0;
21020
21047
  }
21021
- const first = transferSamples[0];
21022
- const last = transferSamples[transferSamples.length - 1];
21023
- const dataSize = last.bytesReceived - first.bytesReceived;
21024
- const timeIntervalSeconds = (last.timestamp - first.timestamp) / 1000;
21025
- return dataSize / timeIntervalSeconds;
21048
+ return totalBytes / (totalTimeMs / 1000);
21026
21049
  }
21027
21050
  }
21028
21051
  exports.ProgressUtils = ProgressUtils;
@@ -28002,7 +28025,7 @@ class PageEventRegistrar {
28002
28025
  registerFileInputEventHandler() {
28003
28026
  const $fileDiv = jQuery("#file-div");
28004
28027
  const $fileNameDiv = $fileDiv.find("#file-name");
28005
- const $fileInput = jQuery("form#uploadForm input[name='multipleFiles']");
28028
+ const $fileInput = jQuery("form#uploadForm input[name='file']");
28006
28029
  $fileInput.on("change", () => {
28007
28030
  this.onFilesChange($fileNameDiv, $fileInput);
28008
28031
  });
@@ -28029,7 +28052,7 @@ class PageEventRegistrar {
28029
28052
  event.preventDefault();
28030
28053
  // wrap async logic in an IIFE
28031
28054
  (async () => {
28032
- const formElement = $('input[name="multipleFiles"]')[0];
28055
+ const formElement = $('input[name="file"]')[0];
28033
28056
  const files = formElement.files;
28034
28057
  if (!files || files.length === 0) {
28035
28058
  (0, toastify_js_1.default)({
@@ -28039,6 +28062,7 @@ class PageEventRegistrar {
28039
28062
  }).showToast();
28040
28063
  return;
28041
28064
  }
28065
+ const disableChunked = (jQuery("#disableChunkedUpload").prop("checked") === true);
28042
28066
  // Block form before uploading
28043
28067
  $uploadForm.block({
28044
28068
  message: '<h1 class="upload-block-modal p-2 m-0">Uploading...</h1>'
@@ -28046,7 +28070,12 @@ class PageEventRegistrar {
28046
28070
  try {
28047
28071
  // Upload all files sequentially
28048
28072
  for (const file of Array.from(files)) {
28049
- await this.uploadFile(file);
28073
+ if (disableChunked) {
28074
+ await this.uploadFileNonChunked(file);
28075
+ }
28076
+ else {
28077
+ await this.uploadFile(file);
28078
+ }
28050
28079
  }
28051
28080
  }
28052
28081
  finally {
@@ -28054,7 +28083,7 @@ class PageEventRegistrar {
28054
28083
  $uploadForm.trigger("reset");
28055
28084
  const $fileDiv = jQuery("#file-div");
28056
28085
  const $fileNameDiv = $fileDiv.find("#file-name");
28057
- const $fileInput = jQuery("form#uploadForm input[name='multipleFiles']");
28086
+ const $fileInput = jQuery("form#uploadForm input[name='file']");
28058
28087
  this.onFilesChange($fileNameDiv, $fileInput);
28059
28088
  $uploadForm.unblock();
28060
28089
  }
@@ -28069,6 +28098,32 @@ class PageEventRegistrar {
28069
28098
  });
28070
28099
  });
28071
28100
  }
28101
+ async uploadFileNonChunked(file) {
28102
+ const formData = new FormData();
28103
+ // Server-side uses formidable({ multiples: true }) so using the same field name is fine
28104
+ formData.append("file", file, file.name);
28105
+ const resp = await fetch("/upload", {
28106
+ method: "POST",
28107
+ body: formData
28108
+ });
28109
+ let data;
28110
+ const contentType = resp.headers.get("content-type") || "";
28111
+ if (contentType.includes("application/json")) {
28112
+ data = await resp.json();
28113
+ }
28114
+ else {
28115
+ data = await resp.text();
28116
+ }
28117
+ if (!resp.ok) {
28118
+ throw new Error(typeof data === "string" ? data : (data?.msg || "Upload failed"));
28119
+ }
28120
+ (0, toastify_js_1.default)({
28121
+ text: `Upload complete: ${file.name}`,
28122
+ duration: -1,
28123
+ close: true,
28124
+ style: { background: "linear-gradient(to right, #00b09b, #96c93d)" }
28125
+ }).showToast();
28126
+ }
28072
28127
  async uploadFile(file) {
28073
28128
  try {
28074
28129
  // Initialize upload