From 9384f5399da39e53855beb8e7f8ded1aa56bf72e Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 27 Dec 2022 14:36:33 +0900 Subject: rename: client -> frontend --- packages/frontend/src/scripts/upload.ts | 137 ++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 packages/frontend/src/scripts/upload.ts (limited to 'packages/frontend/src/scripts/upload.ts') diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts new file mode 100644 index 0000000000..9a39652ef5 --- /dev/null +++ b/packages/frontend/src/scripts/upload.ts @@ -0,0 +1,137 @@ +import { reactive, ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import { readAndCompressImage } from 'browser-image-resizer'; +import { getCompressionConfig } from './upload/compress-config'; +import { defaultStore } from '@/store'; +import { apiUrl } from '@/config'; +import { $i } from '@/account'; +import { alert } from '@/os'; +import { i18n } from '@/i18n'; + +type Uploading = { + id: string; + name: string; + progressMax: number | undefined; + progressValue: number | undefined; + img: string; +}; +export const uploads = ref([]); + +const mimeTypeMap = { + 'image/webp': 'webp', + 'image/jpeg': 'jpg', + 'image/png': 'png', +} as const; + +export function uploadFile( + file: File, + folder?: any, + name?: string, + keepOriginal: boolean = defaultStore.state.keepOriginalUploading, +): Promise { + if ($i == null) throw new Error('Not logged in'); + + if (folder && typeof folder === 'object') folder = folder.id; + + return new Promise((resolve, reject) => { + const id = Math.random().toString(); + + const reader = new FileReader(); + reader.onload = async (): Promise => { + const ctx = reactive({ + id: id, + name: name ?? file.name ?? 'untitled', + progressMax: undefined, + progressValue: undefined, + img: window.URL.createObjectURL(file), + }); + + uploads.value.push(ctx); + + const config = !keepOriginal ? await getCompressionConfig(file) : undefined; + let resizedImage: Blob | undefined; + if (config) { + try { + const resized = await readAndCompressImage(file, config); + if (resized.size < file.size || file.type === 'image/webp') { + // The compression may not always reduce the file size + // (and WebP is not browser safe yet) + resizedImage = resized; + } + if (_DEV_) { + const saved = ((1 - resized.size / file.size) * 100).toFixed(2); + console.log(`Image compression: before ${file.size} bytes, after ${resized.size} bytes, saved ${saved}%`); + } + + ctx.name = file.type !== config.mimeType ? `${ctx.name}.${mimeTypeMap[config.mimeType]}` : ctx.name; + } catch (err) { + console.error('Failed to resize image', err); + } + } + + const formData = new FormData(); + formData.append('i', $i.token); + formData.append('force', 'true'); + formData.append('file', resizedImage ?? file); + formData.append('name', ctx.name); + if (folder) formData.append('folderId', folder); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', apiUrl + '/drive/files/create', true); + xhr.onload = ((ev: ProgressEvent) => { + if (xhr.status !== 200 || ev.target == null || ev.target.response == null) { + // TODO: 消すのではなくて(ネットワーク的なエラーなら)再送できるようにしたい + uploads.value = uploads.value.filter(x => x.id !== id); + + if (ev.target?.response) { + const res = JSON.parse(ev.target.response); + if (res.error?.id === 'bec5bd69-fba3-43c9-b4fb-2894b66ad5d2') { + alert({ + type: 'error', + title: i18n.ts.failedToUpload, + text: i18n.ts.cannotUploadBecauseInappropriate, + }); + } else if (res.error?.id === 'd08dbc37-a6a9-463a-8c47-96c32ab5f064') { + alert({ + type: 'error', + title: i18n.ts.failedToUpload, + text: i18n.ts.cannotUploadBecauseNoFreeSpace, + }); + } else { + alert({ + type: 'error', + title: i18n.ts.failedToUpload, + text: `${res.error?.message}\n${res.error?.code}\n${res.error?.id}`, + }); + } + } else { + alert({ + type: 'error', + title: 'Failed to upload', + text: `${JSON.stringify(ev.target?.response)}, ${JSON.stringify(xhr.response)}`, + }); + } + + reject(); + return; + } + + const driveFile = JSON.parse(ev.target.response); + + resolve(driveFile); + + uploads.value = uploads.value.filter(x => x.id !== id); + }) as (ev: ProgressEvent) => any; + + xhr.upload.onprogress = ev => { + if (ev.lengthComputable) { + ctx.progressMax = ev.total; + ctx.progressValue = ev.loaded; + } + }; + + xhr.send(formData); + }; + reader.readAsArrayBuffer(file); + }); +} -- cgit v1.2.3-freya