From 05fb6f096db7c58698f8e0bc7fc79e129b241176 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Apr 2018 00:43:36 +0200 Subject: [PATCH] Resize images before upload in web UI to reduce bandwidth (#7223) * Resize images before upload in web UI to reduce bandwidth Fix #7218 * Fix issues * Do not resize GIFs in JS --- app/javascript/mastodon/actions/compose.js | 95 +++++++++++++++++++--- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index eee9c6928c..c8ea5aaba7 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -174,6 +174,79 @@ export function submitComposeFail(error) { }; }; +const MAX_IMAGE_DIMENSION = 1280; + +const dataURLtoBlob = dataURL => { + const BASE64_MARKER = ';base64,'; + + if (dataURL.indexOf(BASE64_MARKER) === -1) { + const parts = dataURL.split(','); + const contentType = parts[0].split(':')[1]; + const raw = parts[1]; + + return new Blob([raw], { type: contentType }); + } + + const parts = dataURL.split(BASE64_MARKER); + const contentType = parts[0].split(':')[1]; + const raw = window.atob(parts[1]); + const rawLength = raw.length; + + const uInt8Array = new Uint8Array(rawLength); + + for (let i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return new Blob([uInt8Array], { type: contentType }); +}; + +const resizeImage = (inputFile, callback) => { + if (inputFile.type.match(/image.*/) && inputFile.type !== 'image/gif') { + const reader = new FileReader(); + + reader.onload = e => { + const img = new Image(); + + img.onload = () => { + const canvas = document.createElement('canvas'); + const { width, height } = img; + + let newWidth, newHeight; + + if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) { + callback(inputFile); + return; + } + + if (width > height) { + newHeight = height * MAX_IMAGE_DIMENSION / width; + newWidth = MAX_IMAGE_DIMENSION; + } else if (height > width) { + newWidth = width * MAX_IMAGE_DIMENSION / height; + newHeight = MAX_IMAGE_DIMENSION; + } else { + newWidth = MAX_IMAGE_DIMENSION; + newHeight = MAX_IMAGE_DIMENSION; + } + + canvas.width = newWidth; + canvas.height = newHeight; + + canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight); + + callback(dataURLtoBlob(canvas.toDataURL(inputFile.type))); + }; + + img.src = e.target.result; + }; + + reader.readAsDataURL(inputFile); + } else { + callback(inputFile); + } +}; + export function uploadCompose(files) { return function (dispatch, getState) { if (getState().getIn(['compose', 'media_attachments']).size > 3) { @@ -182,17 +255,19 @@ export function uploadCompose(files) { dispatch(uploadComposeRequest()); - let data = new FormData(); - data.append('file', files[0]); + resizeImage(files[0], file => { + let data = new FormData(); + data.append('file', file); - api(getState).post('/api/v1/media', data, { - onUploadProgress: function (e) { - dispatch(uploadComposeProgress(e.loaded, e.total)); - }, - }).then(function (response) { - dispatch(uploadComposeSuccess(response.data)); - }).catch(function (error) { - dispatch(uploadComposeFail(error)); + api(getState).post('/api/v1/media', data, { + onUploadProgress: function (e) { + dispatch(uploadComposeProgress(e.loaded, e.total)); + }, + }).then(function (response) { + dispatch(uploadComposeSuccess(response.data)); + }).catch(function (error) { + dispatch(uploadComposeFail(error)); + }); }); }; };