support file downloads on iOS through data: uris

This commit is contained in:
Bruno Windels 2021-03-04 21:51:29 +01:00
parent 4b075e582e
commit 6ad778d27b
3 changed files with 50 additions and 7 deletions

View file

@ -7,16 +7,40 @@
<a id="link" href="#">Download</a> <a id="link" href="#">Download</a>
<script type="text/javascript"> <script type="text/javascript">
var link = document.getElementById("link"); var link = document.getElementById("link");
function download(blob, filename) {
function downloadBlob(blob, filename) {
var url = URL.createObjectURL(blob); var url = URL.createObjectURL(blob);
link.href = url; link.href = url;
link.download = filename; link.download = filename;
link.innerText = url;
link.click(); link.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
window.addEventListener("message", function(event) {
if (event.origin === window.location.origin && event.data.type === "download") { function toBase64(buffer) {
download(event.data.blob, event.data.filename); const bytes = new Uint8Array(buffer);
let binaryStr = "";
for (let i = 0; i < bytes.byteLength; i++) {
binaryStr += String.fromCharCode(bytes[i]);
}
return btoa(binaryStr);
}
function downloadBuffer(buffer, mimeType, filename) {
var url = "data:" + mimeType + ";base64," + toBase64(buffer);
link.href = url;
link.download = filename;
link.innerText = url;
link.click();
}
window.addEventListener("message", async function(event) {
if (event.origin === window.location.origin) {
if (event.data.type === "downloadBuffer") {
await downloadBuffer(event.data.buffer, event.data.mimeType, event.data.filename);
} else if (event.data.type === "downloadBlob") {
await downloadBlob(event.data.blob, event.data.filename);
}
} }
}); });
</script> </script>

View file

@ -150,7 +150,7 @@ export class Platform {
if (navigator.msSaveBlob) { if (navigator.msSaveBlob) {
navigator.msSaveBlob(blobHandle.nativeBlob, filename); navigator.msSaveBlob(blobHandle.nativeBlob, filename);
} else { } else {
downloadInIframe(this._container, this._paths.downloadSandbox, blobHandle.nativeBlob, filename); downloadInIframe(this._container, this._paths.downloadSandbox, blobHandle, filename);
} }
} }

View file

@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export async function downloadInIframe(container, iframeSrc, blob, filename) { // From https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885
const isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) && !window.MSStream;
export async function downloadInIframe(container, iframeSrc, blobHandle, filename) {
let iframe = container.querySelector("iframe.downloadSandbox"); let iframe = container.querySelector("iframe.downloadSandbox");
if (!iframe) { if (!iframe) {
iframe = document.createElement("iframe"); iframe = document.createElement("iframe");
@ -33,5 +36,21 @@ export async function downloadInIframe(container, iframeSrc, blob, filename) {
}); });
detach(); detach();
} }
iframe.contentWindow.postMessage({type: "download", blob: blob, filename: filename}, "*"); if (isIOS) {
// iOS can't read a blob in a sandboxed iframe,
// see https://github.com/vector-im/hydrogen-web/issues/244
const buffer = await blobHandle.readAsBuffer();
iframe.contentWindow.postMessage({
type: "downloadBuffer",
buffer,
mimeType: blobHandle.mimeType,
filename: filename
}, "*");
} else {
iframe.contentWindow.postMessage({
type: "downloadBlob",
blob: blobHandle.nativeBlob,
filename: filename
}, "*");
}
} }