Merge pull request #260 from vector-im/bwindels/fix-244
Make file uploads and downloads work on iOS
This commit is contained in:
commit
db8de005a7
4 changed files with 52 additions and 9 deletions
|
@ -7,16 +7,38 @@
|
||||||
<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.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.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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,8 @@ const ALLOWED_BLOB_MIMETYPES = {
|
||||||
'audio/x-flac': true,
|
'audio/x-flac': true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_MIMETYPE = 'application/octet-stream';
|
||||||
|
|
||||||
export class BlobHandle {
|
export class BlobHandle {
|
||||||
constructor(blob, buffer = null) {
|
constructor(blob, buffer = null) {
|
||||||
this._blob = blob;
|
this._blob = blob;
|
||||||
|
@ -79,7 +81,7 @@ export class BlobHandle {
|
||||||
static fromBuffer(buffer, mimetype) {
|
static fromBuffer(buffer, mimetype) {
|
||||||
mimetype = mimetype ? mimetype.split(";")[0].trim() : '';
|
mimetype = mimetype ? mimetype.split(";")[0].trim() : '';
|
||||||
if (!ALLOWED_BLOB_MIMETYPES[mimetype]) {
|
if (!ALLOWED_BLOB_MIMETYPES[mimetype]) {
|
||||||
mimetype = 'application/octet-stream';
|
mimetype = DEFAULT_MIMETYPE;
|
||||||
}
|
}
|
||||||
return new BlobHandle(new Blob([buffer], {type: mimetype}), buffer);
|
return new BlobHandle(new Blob([buffer], {type: mimetype}), buffer);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +121,7 @@ export class BlobHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
get mimeType() {
|
get mimeType() {
|
||||||
return this._blob.type;
|
return this._blob.type || DEFAULT_MIMETYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
|
|
@ -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
|
||||||
|
}, "*");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue