Changed Download Handler Behavior (#2964)
- Updated Handler for files with size of 49GB or less - Used Browser Handler tor size of more than 50GB Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -13,7 +13,6 @@
|
||||
"@mui/styles": "^5.12.0",
|
||||
"@mui/x-date-pickers": "^5.0.20",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@types/streamsaver": "^2.0.1",
|
||||
"@uiw/react-textarea-code-editor": "^2.1.1",
|
||||
"kbar": "^0.1.0-beta.39",
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
@@ -33,7 +32,6 @@
|
||||
"react-window": "^1.8.9",
|
||||
"react-window-infinite-loader": "^1.0.9",
|
||||
"recharts": "^2.6.2",
|
||||
"streamsaver": "^2.0.6",
|
||||
"styled-components": "^5.3.11",
|
||||
"superagent": "^8.0.8",
|
||||
"tinycolor2": "^1.6.0",
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import { BucketObjectItem } from "./ListObjects/types";
|
||||
import { encodeURLString } from "../../../../../common/utils";
|
||||
import { removeTrace } from "../../../ObjectBrowser/transferManager";
|
||||
import streamSaver from "streamsaver";
|
||||
import store from "../../../../../store";
|
||||
import { PermissionResource } from "api/consoleApi";
|
||||
|
||||
@@ -50,146 +49,101 @@ export const download = (
|
||||
if (versionID) {
|
||||
path = path.concat(`&version_id=${versionID}`);
|
||||
}
|
||||
return new DownloadHelper(
|
||||
path,
|
||||
id,
|
||||
anonymousMode,
|
||||
fileSize,
|
||||
progressCallback,
|
||||
completeCallback,
|
||||
errorCallback,
|
||||
abortCallback,
|
||||
toastCallback,
|
||||
|
||||
// If file is greater than 50GiB then we force browser download, if not then we use HTTP Request for Object Manager
|
||||
if (fileSize > 53687091200) {
|
||||
return new BrowserDownload(path, id, completeCallback, toastCallback);
|
||||
}
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.open("GET", path, true);
|
||||
if (anonymousMode) {
|
||||
req.setRequestHeader("X-Anonymous", "1");
|
||||
}
|
||||
req.addEventListener(
|
||||
"progress",
|
||||
function (evt) {
|
||||
let percentComplete = Math.round((evt.loaded / fileSize) * 100);
|
||||
|
||||
if (progressCallback) {
|
||||
progressCallback(percentComplete);
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
req.responseType = "blob";
|
||||
req.onreadystatechange = () => {
|
||||
if (req.readyState === 4) {
|
||||
if (req.status === 200) {
|
||||
const rspHeader = req.getResponseHeader("Content-Disposition");
|
||||
|
||||
let filename = "download";
|
||||
if (rspHeader) {
|
||||
let rspHeaderDecoded = decodeURIComponent(rspHeader);
|
||||
filename = rspHeaderDecoded.split('"')[1];
|
||||
}
|
||||
|
||||
if (completeCallback) {
|
||||
completeCallback();
|
||||
}
|
||||
|
||||
removeTrace(id);
|
||||
|
||||
var link = document.createElement("a");
|
||||
link.href = window.URL.createObjectURL(req.response);
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} else {
|
||||
if (req.getResponseHeader("Content-Type") === "application/json") {
|
||||
const rspBody: { detailedMessage?: string } = JSON.parse(
|
||||
req.response,
|
||||
);
|
||||
if (rspBody.detailedMessage) {
|
||||
errorCallback(rspBody.detailedMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
errorCallback(`Unexpected response status code (${req.status}).`);
|
||||
}
|
||||
}
|
||||
};
|
||||
req.onerror = () => {
|
||||
if (errorCallback) {
|
||||
errorCallback("A network error occurred.");
|
||||
}
|
||||
};
|
||||
req.onabort = () => {
|
||||
if (abortCallback) {
|
||||
abortCallback();
|
||||
}
|
||||
};
|
||||
|
||||
return req;
|
||||
};
|
||||
|
||||
class DownloadHelper {
|
||||
aborter: AbortController;
|
||||
class BrowserDownload {
|
||||
path: string;
|
||||
id: string;
|
||||
filename: string = "";
|
||||
anonymousMode: boolean;
|
||||
fileSize: number = 0;
|
||||
writer: any = null;
|
||||
progressCallback: (progress: number) => void;
|
||||
completeCallback: () => void;
|
||||
errorCallback: (msg: string) => void;
|
||||
abortCallback: () => void;
|
||||
toastCallback: () => void;
|
||||
|
||||
constructor(
|
||||
path: string,
|
||||
id: string,
|
||||
anonymousMode: boolean,
|
||||
fileSize: number,
|
||||
progressCallback: (progress: number) => void,
|
||||
completeCallback: () => void,
|
||||
errorCallback: (msg: string) => void,
|
||||
abortCallback: () => void,
|
||||
toastCallback: () => void,
|
||||
) {
|
||||
this.aborter = new AbortController();
|
||||
this.path = path;
|
||||
this.id = id;
|
||||
this.anonymousMode = anonymousMode;
|
||||
this.fileSize = fileSize;
|
||||
this.progressCallback = progressCallback;
|
||||
this.completeCallback = completeCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
this.abortCallback = abortCallback;
|
||||
this.toastCallback = toastCallback;
|
||||
}
|
||||
|
||||
abort(): void {
|
||||
this.aborter.abort();
|
||||
this.abortCallback();
|
||||
if (this.writer) {
|
||||
this.writer.abort();
|
||||
}
|
||||
}
|
||||
|
||||
send(): void {
|
||||
let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
if (isSafari) {
|
||||
this.toastCallback();
|
||||
this.downloadByBrowser();
|
||||
} else if (!this.fileSize) {
|
||||
this.downloadByBrowser();
|
||||
} else {
|
||||
this.download({
|
||||
url: this.path,
|
||||
chunkSize: 1024 * 1024 * 1024 * 1.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getRangeContent(url: string, start: number, end: number) {
|
||||
const info = this.getRequestInfo(start, end);
|
||||
const response = await fetch(url, info);
|
||||
if (response.ok && response.body) {
|
||||
if (!this.filename) {
|
||||
this.filename = this.getFilename(response);
|
||||
}
|
||||
if (!this.writer) {
|
||||
this.writer = streamSaver.createWriteStream(this.filename).getWriter();
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
let done, value;
|
||||
while (!done) {
|
||||
({ value, done } = await reader.read());
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
await this.writer.write(value);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unexpected response status code (${response.status}).`);
|
||||
}
|
||||
}
|
||||
|
||||
getRequestInfo(start: number, end: number) {
|
||||
const info: RequestInit = {
|
||||
signal: this.aborter.signal,
|
||||
headers: { range: `bytes=${start}-${end}` },
|
||||
};
|
||||
if (this.anonymousMode) {
|
||||
info.headers = { ...info.headers, "X-Anonymous": "1" };
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
getFilename(response: Response) {
|
||||
const rspHeader = response.headers.get("Content-Disposition");
|
||||
if (rspHeader) {
|
||||
let rspHeaderDecoded = decodeURIComponent(rspHeader);
|
||||
return rspHeaderDecoded.split('"')[1];
|
||||
}
|
||||
return "download";
|
||||
}
|
||||
|
||||
async download({ url, chunkSize }: any) {
|
||||
const numberOfChunks = Math.ceil(this.fileSize / chunkSize);
|
||||
this.progressCallback(0);
|
||||
try {
|
||||
for (let i = 0; i < numberOfChunks; i++) {
|
||||
let start = i * chunkSize;
|
||||
let end =
|
||||
i + 1 === numberOfChunks
|
||||
? this.fileSize - 1
|
||||
: (i + 1) * chunkSize - 1;
|
||||
await this.getRangeContent(url, start, end);
|
||||
let percentComplete = Math.round(((i + 1) / numberOfChunks) * 100);
|
||||
this.progressCallback(percentComplete);
|
||||
}
|
||||
this.writer.close();
|
||||
this.completeCallback();
|
||||
removeTrace(this.id);
|
||||
} catch (e: any) {
|
||||
this.errorCallback(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
downloadByBrowser() {
|
||||
this.toastCallback();
|
||||
const link = document.createElement("a");
|
||||
link.href = this.path;
|
||||
document.body.appendChild(link);
|
||||
|
||||
@@ -2782,11 +2782,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/streamsaver@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/streamsaver/-/streamsaver-2.0.1.tgz#fa5e5b891d1b282be3078c232a30ee004b8e0be0"
|
||||
integrity sha512-I49NtT8w6syBI3Zg3ixCyygTHoTVMY0z2TMRcTgccdIsVd2MwlKk7ITLHLsJtgchUHcOd7QEARG9h0ifcA6l2Q==
|
||||
|
||||
"@types/styled-components@^5.1.25":
|
||||
version "5.1.26"
|
||||
resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.26.tgz#5627e6812ee96d755028a98dae61d28e57c233af"
|
||||
@@ -11115,11 +11110,6 @@ statuses@2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
|
||||
|
||||
streamsaver@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/streamsaver/-/streamsaver-2.0.6.tgz#869d2347dd70191e0ac888d52296956a8cba2ed9"
|
||||
integrity sha512-LK4e7TfCV8HzuM0PKXuVUfKyCB1FtT9L0EGxsFk5Up8njj0bXK8pJM9+Wq2Nya7/jslmCQwRK39LFm55h7NBTw==
|
||||
|
||||
strict-uri-encode@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||
|
||||
Reference in New Issue
Block a user