mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-24 19:33:02 +03:00
🐛 Fixed images sometimes being stored as data:
URLs when copy/pasting from other editors (#16707)
refs https://github.com/TryGhost/Team/issues/2887 Images could sometimes be pasted into the editor (noticed especially with Google Docs) with `data:` URLs rather than typical `https:` URLs. That causes problems because data URLs are large binary blobs that get stored in the `posts` table and passed through many areas of the system that doesn't expect large binary blobs, causing knock-on effects. - added handling to our editor's image card to detect when the card is displayed in the editor with a `data:` URL and if it was then it converts it to a file and uploads it so the image can be stored and displayed the same way as any other image - handles uploads on both paste and opening a post in the editor that was previously saved with a `data:` URL
This commit is contained in:
parent
1f643884af
commit
11cab899f0
@ -5,6 +5,7 @@ import {
|
||||
IMAGE_MIME_TYPES
|
||||
} from 'ghost-admin/components/gh-image-uploader';
|
||||
import {action, computed, set, setProperties} from '@ember/object';
|
||||
import {fetch} from 'fetch';
|
||||
import {utils as ghostHelperUtils} from '@tryghost/helpers';
|
||||
import {isEmpty} from '@ember/utils';
|
||||
import {run} from '@ember/runloop';
|
||||
@ -12,6 +13,30 @@ import {inject as service} from '@ember/service';
|
||||
|
||||
const {countWords} = ghostHelperUtils;
|
||||
|
||||
async function dataSrcToFile(src, fileName) {
|
||||
if (!src.startsWith('data:')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mimeType = src.split(',')[0].split(':')[1].split(';')[0];
|
||||
|
||||
if (!fileName) {
|
||||
let uuid;
|
||||
try {
|
||||
uuid = window.crypto.randomUUID();
|
||||
} catch (e) {
|
||||
uuid = Math.random().toString(36).substring(2, 15);
|
||||
}
|
||||
const extension = mimeType.split('/')[1];
|
||||
fileName = `data-src-image-${uuid}.${extension}`;
|
||||
}
|
||||
|
||||
const blob = await fetch(src).then(it => it.blob());
|
||||
const file = new File([blob], fileName, {type: mimeType, lastModified: new Date()});
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
@classic
|
||||
export default class KoenigCardImage extends Component {
|
||||
@service ui;
|
||||
@ -159,11 +184,21 @@ export default class KoenigCardImage extends Component {
|
||||
didReceiveAttrs() {
|
||||
super.didReceiveAttrs(...arguments);
|
||||
|
||||
// `payload.files` can be set if we have an externaly set image that
|
||||
// if payload.src is a data attribute something has gone wrong and we're
|
||||
// storing binary data in the payload. Grab the data and upload it to
|
||||
// convert to a proper ULR
|
||||
if (this.payload.src?.startsWith('data:')) {
|
||||
const file = dataSrcToFile(this.payload.src, this.payload.fileName, this.payload.mimeType);
|
||||
this.payload.files = [file];
|
||||
}
|
||||
|
||||
// `payload.files` can be set if we have an externally set image that
|
||||
// should be uploaded. Typical example would be from a paste or drag/drop
|
||||
if (!isEmpty(this.payload.files)) {
|
||||
run.schedule('afterRender', this, function () {
|
||||
this.set('files', this.payload.files);
|
||||
run.schedule('afterRender', this, async function () {
|
||||
// files can be a promise if converted from data:
|
||||
const files = await Promise.all(this.payload.files);
|
||||
this.set('files', files);
|
||||
|
||||
// we don't want to persist any file data in the document
|
||||
delete this.payload.files;
|
||||
|
Loading…
Reference in New Issue
Block a user