|
17 | 17 | import { BucketObjectItem } from "./ListObjects/types";
|
18 | 18 | import { encodeURLString } from "../../../../../common/utils";
|
19 | 19 | import { removeTrace } from "../../../ObjectBrowser/transferManager";
|
20 |
| -import streamSaver from "streamsaver"; |
21 | 20 | import store from "../../../../../store";
|
22 | 21 | import { PermissionResource } from "api/consoleApi";
|
23 | 22 |
|
@@ -50,146 +49,101 @@ export const download = (
|
50 | 49 | if (versionID) {
|
51 | 50 | path = path.concat(`&version_id=${versionID}`);
|
52 | 51 | }
|
53 |
| - return new DownloadHelper( |
54 |
| - path, |
55 |
| - id, |
56 |
| - anonymousMode, |
57 |
| - fileSize, |
58 |
| - progressCallback, |
59 |
| - completeCallback, |
60 |
| - errorCallback, |
61 |
| - abortCallback, |
62 |
| - toastCallback, |
| 52 | + |
| 53 | + // If file is greater than 50GiB then we force browser download, if not then we use HTTP Request for Object Manager |
| 54 | + if (fileSize > 53687091200) { |
| 55 | + return new BrowserDownload(path, id, completeCallback, toastCallback); |
| 56 | + } |
| 57 | + |
| 58 | + let req = new XMLHttpRequest(); |
| 59 | + req.open("GET", path, true); |
| 60 | + if (anonymousMode) { |
| 61 | + req.setRequestHeader("X-Anonymous", "1"); |
| 62 | + } |
| 63 | + req.addEventListener( |
| 64 | + "progress", |
| 65 | + function (evt) { |
| 66 | + let percentComplete = Math.round((evt.loaded / fileSize) * 100); |
| 67 | + |
| 68 | + if (progressCallback) { |
| 69 | + progressCallback(percentComplete); |
| 70 | + } |
| 71 | + }, |
| 72 | + false, |
63 | 73 | );
|
| 74 | + |
| 75 | + req.responseType = "blob"; |
| 76 | + req.onreadystatechange = () => { |
| 77 | + if (req.readyState === 4) { |
| 78 | + if (req.status === 200) { |
| 79 | + const rspHeader = req.getResponseHeader("Content-Disposition"); |
| 80 | + |
| 81 | + let filename = "download"; |
| 82 | + if (rspHeader) { |
| 83 | + let rspHeaderDecoded = decodeURIComponent(rspHeader); |
| 84 | + filename = rspHeaderDecoded.split('"')[1]; |
| 85 | + } |
| 86 | + |
| 87 | + if (completeCallback) { |
| 88 | + completeCallback(); |
| 89 | + } |
| 90 | + |
| 91 | + removeTrace(id); |
| 92 | + |
| 93 | + var link = document.createElement("a"); |
| 94 | + link.href = window.URL.createObjectURL(req.response); |
| 95 | + link.download = filename; |
| 96 | + document.body.appendChild(link); |
| 97 | + link.click(); |
| 98 | + document.body.removeChild(link); |
| 99 | + } else { |
| 100 | + if (req.getResponseHeader("Content-Type") === "application/json") { |
| 101 | + const rspBody: { detailedMessage?: string } = JSON.parse( |
| 102 | + req.response, |
| 103 | + ); |
| 104 | + if (rspBody.detailedMessage) { |
| 105 | + errorCallback(rspBody.detailedMessage); |
| 106 | + return; |
| 107 | + } |
| 108 | + } |
| 109 | + errorCallback(`Unexpected response status code (${req.status}).`); |
| 110 | + } |
| 111 | + } |
| 112 | + }; |
| 113 | + req.onerror = () => { |
| 114 | + if (errorCallback) { |
| 115 | + errorCallback("A network error occurred."); |
| 116 | + } |
| 117 | + }; |
| 118 | + req.onabort = () => { |
| 119 | + if (abortCallback) { |
| 120 | + abortCallback(); |
| 121 | + } |
| 122 | + }; |
| 123 | + |
| 124 | + return req; |
64 | 125 | };
|
65 | 126 |
|
66 |
| -class DownloadHelper { |
67 |
| - aborter: AbortController; |
| 127 | +class BrowserDownload { |
68 | 128 | path: string;
|
69 | 129 | id: string;
|
70 |
| - filename: string = ""; |
71 |
| - anonymousMode: boolean; |
72 |
| - fileSize: number = 0; |
73 |
| - writer: any = null; |
74 |
| - progressCallback: (progress: number) => void; |
75 | 130 | completeCallback: () => void;
|
76 |
| - errorCallback: (msg: string) => void; |
77 |
| - abortCallback: () => void; |
78 | 131 | toastCallback: () => void;
|
79 | 132 |
|
80 | 133 | constructor(
|
81 | 134 | path: string,
|
82 | 135 | id: string,
|
83 |
| - anonymousMode: boolean, |
84 |
| - fileSize: number, |
85 |
| - progressCallback: (progress: number) => void, |
86 | 136 | completeCallback: () => void,
|
87 |
| - errorCallback: (msg: string) => void, |
88 |
| - abortCallback: () => void, |
89 | 137 | toastCallback: () => void,
|
90 | 138 | ) {
|
91 |
| - this.aborter = new AbortController(); |
92 | 139 | this.path = path;
|
93 | 140 | this.id = id;
|
94 |
| - this.anonymousMode = anonymousMode; |
95 |
| - this.fileSize = fileSize; |
96 |
| - this.progressCallback = progressCallback; |
97 | 141 | this.completeCallback = completeCallback;
|
98 |
| - this.errorCallback = errorCallback; |
99 |
| - this.abortCallback = abortCallback; |
100 | 142 | this.toastCallback = toastCallback;
|
101 | 143 | }
|
102 | 144 |
|
103 |
| - abort(): void { |
104 |
| - this.aborter.abort(); |
105 |
| - this.abortCallback(); |
106 |
| - if (this.writer) { |
107 |
| - this.writer.abort(); |
108 |
| - } |
109 |
| - } |
110 |
| - |
111 | 145 | send(): void {
|
112 |
| - let isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); |
113 |
| - if (isSafari) { |
114 |
| - this.toastCallback(); |
115 |
| - this.downloadByBrowser(); |
116 |
| - } else if (!this.fileSize) { |
117 |
| - this.downloadByBrowser(); |
118 |
| - } else { |
119 |
| - this.download({ |
120 |
| - url: this.path, |
121 |
| - chunkSize: 1024 * 1024 * 1024 * 1.5, |
122 |
| - }); |
123 |
| - } |
124 |
| - } |
125 |
| - |
126 |
| - async getRangeContent(url: string, start: number, end: number) { |
127 |
| - const info = this.getRequestInfo(start, end); |
128 |
| - const response = await fetch(url, info); |
129 |
| - if (response.ok && response.body) { |
130 |
| - if (!this.filename) { |
131 |
| - this.filename = this.getFilename(response); |
132 |
| - } |
133 |
| - if (!this.writer) { |
134 |
| - this.writer = streamSaver.createWriteStream(this.filename).getWriter(); |
135 |
| - } |
136 |
| - const reader = response.body.getReader(); |
137 |
| - let done, value; |
138 |
| - while (!done) { |
139 |
| - ({ value, done } = await reader.read()); |
140 |
| - if (done) { |
141 |
| - break; |
142 |
| - } |
143 |
| - await this.writer.write(value); |
144 |
| - } |
145 |
| - } else { |
146 |
| - throw new Error(`Unexpected response status code (${response.status}).`); |
147 |
| - } |
148 |
| - } |
149 |
| - |
150 |
| - getRequestInfo(start: number, end: number) { |
151 |
| - const info: RequestInit = { |
152 |
| - signal: this.aborter.signal, |
153 |
| - headers: { range: `bytes=${start}-${end}` }, |
154 |
| - }; |
155 |
| - if (this.anonymousMode) { |
156 |
| - info.headers = { ...info.headers, "X-Anonymous": "1" }; |
157 |
| - } |
158 |
| - return info; |
159 |
| - } |
160 |
| - |
161 |
| - getFilename(response: Response) { |
162 |
| - const rspHeader = response.headers.get("Content-Disposition"); |
163 |
| - if (rspHeader) { |
164 |
| - let rspHeaderDecoded = decodeURIComponent(rspHeader); |
165 |
| - return rspHeaderDecoded.split('"')[1]; |
166 |
| - } |
167 |
| - return "download"; |
168 |
| - } |
169 |
| - |
170 |
| - async download({ url, chunkSize }: any) { |
171 |
| - const numberOfChunks = Math.ceil(this.fileSize / chunkSize); |
172 |
| - this.progressCallback(0); |
173 |
| - try { |
174 |
| - for (let i = 0; i < numberOfChunks; i++) { |
175 |
| - let start = i * chunkSize; |
176 |
| - let end = |
177 |
| - i + 1 === numberOfChunks |
178 |
| - ? this.fileSize - 1 |
179 |
| - : (i + 1) * chunkSize - 1; |
180 |
| - await this.getRangeContent(url, start, end); |
181 |
| - let percentComplete = Math.round(((i + 1) / numberOfChunks) * 100); |
182 |
| - this.progressCallback(percentComplete); |
183 |
| - } |
184 |
| - this.writer.close(); |
185 |
| - this.completeCallback(); |
186 |
| - removeTrace(this.id); |
187 |
| - } catch (e: any) { |
188 |
| - this.errorCallback(e.message); |
189 |
| - } |
190 |
| - } |
191 |
| - |
192 |
| - downloadByBrowser() { |
| 146 | + this.toastCallback(); |
193 | 147 | const link = document.createElement("a");
|
194 | 148 | link.href = this.path;
|
195 | 149 | document.body.appendChild(link);
|
|
0 commit comments