mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-15 12:53:51 +02:00
199 lines
5.7 KiB
JavaScript
199 lines
5.7 KiB
JavaScript
import { toast } from 'vue-sonner';
|
|
|
|
import { $throw } from '../services/request';
|
|
import { AppDebug } from '../services/appConfig.js';
|
|
import { extractFileId } from '../shared/utils';
|
|
import { imageRequest } from '../api';
|
|
|
|
function resolveMessage(message) {
|
|
if (typeof message === 'function') {
|
|
return message();
|
|
}
|
|
return message;
|
|
}
|
|
|
|
function getInputElement(selector) {
|
|
if (!selector) {
|
|
return null;
|
|
}
|
|
if (typeof selector === 'function') {
|
|
return selector();
|
|
}
|
|
if (typeof selector === 'string') {
|
|
return document.querySelector(selector);
|
|
}
|
|
return selector;
|
|
}
|
|
|
|
export function handleImageUploadInput(event, options = {}) {
|
|
const {
|
|
inputSelector,
|
|
// 20MB
|
|
maxSize = 20000000,
|
|
acceptPattern = /image.*/,
|
|
tooLargeMessage,
|
|
invalidTypeMessage,
|
|
onClear
|
|
} = options;
|
|
|
|
const clearInput = () => {
|
|
onClear?.();
|
|
const input = getInputElement(inputSelector);
|
|
if (input) {
|
|
input.value = '';
|
|
}
|
|
};
|
|
|
|
const files = event?.target?.files || event?.dataTransfer?.files;
|
|
if (!files || files.length === 0) {
|
|
clearInput();
|
|
return { file: null, clearInput };
|
|
}
|
|
|
|
const file = files[0];
|
|
if (file.size >= maxSize) {
|
|
if (tooLargeMessage) {
|
|
toast.error(resolveMessage(tooLargeMessage));
|
|
}
|
|
clearInput();
|
|
return { file: null, clearInput };
|
|
}
|
|
|
|
let acceptRegex = null;
|
|
if (acceptPattern) {
|
|
acceptRegex =
|
|
acceptPattern instanceof RegExp
|
|
? acceptPattern
|
|
: new RegExp(acceptPattern);
|
|
}
|
|
|
|
if (acceptRegex && !acceptRegex.test(file.type)) {
|
|
if (invalidTypeMessage) {
|
|
toast.error(resolveMessage(invalidTypeMessage));
|
|
}
|
|
clearInput();
|
|
return { file: null, clearInput };
|
|
}
|
|
|
|
return { file, clearInput };
|
|
}
|
|
|
|
/**
|
|
* @param {string} base64Data - base64 encoded image
|
|
* @returns {Promise<string>} resized base64 encoded image
|
|
*/
|
|
export async function resizeImageToFitLimits(base64Data) {
|
|
// frontend limit check = 20MB
|
|
return AppApi.ResizeImageToFitLimits(base64Data);
|
|
}
|
|
|
|
/**
|
|
* Upload image through AWS
|
|
* @param {'avatar'|'world'} type
|
|
* @param {object} opts
|
|
* @param {string} opts.entityId - avatar or world id
|
|
* @param {string} opts.imageUrl - current imageUrl on the entity
|
|
* @param {string} opts.base64File - base64 encoded image data
|
|
* @param {Blob} opts.blob - the original blob (used for file size)
|
|
*/
|
|
export async function uploadImageLegacy(
|
|
type,
|
|
{ entityId, imageUrl, base64File, blob }
|
|
) {
|
|
const apiMap = {
|
|
avatar: {
|
|
uploadImage: imageRequest.uploadAvatarImage,
|
|
fileStart: imageRequest.uploadAvatarImageFileStart,
|
|
fileFinish: imageRequest.uploadAvatarImageFileFinish,
|
|
sigStart: imageRequest.uploadAvatarImageSigStart,
|
|
sigFinish: imageRequest.uploadAvatarImageSigFinish,
|
|
setImage: imageRequest.setAvatarImage
|
|
},
|
|
world: {
|
|
uploadImage: imageRequest.uploadWorldImage,
|
|
fileStart: imageRequest.uploadWorldImageFileStart,
|
|
fileFinish: imageRequest.uploadWorldImageFileFinish,
|
|
sigStart: imageRequest.uploadWorldImageSigStart,
|
|
sigFinish: imageRequest.uploadWorldImageSigFinish,
|
|
setImage: imageRequest.setWorldImage
|
|
}
|
|
};
|
|
const api = apiMap[type];
|
|
|
|
const fileMd5 = await AppApi.MD5File(base64File);
|
|
const fileSizeInBytes = parseInt(blob.size, 10);
|
|
const base64SignatureFile = await AppApi.SignFile(base64File);
|
|
const signatureMd5 = await AppApi.MD5File(base64SignatureFile);
|
|
const signatureSizeInBytes = parseInt(
|
|
await AppApi.FileLength(base64SignatureFile),
|
|
10
|
|
);
|
|
const fileId = extractFileId(imageUrl);
|
|
|
|
// imageInit
|
|
const uploadRes = await api.uploadImage(
|
|
{ fileMd5, fileSizeInBytes, signatureMd5, signatureSizeInBytes },
|
|
fileId
|
|
);
|
|
const uploadedFileId = uploadRes.json.id;
|
|
const fileVersion =
|
|
uploadRes.json.versions[uploadRes.json.versions.length - 1].version;
|
|
|
|
// imageFileStart
|
|
const fileStartRes = await api.fileStart({
|
|
fileId: uploadedFileId,
|
|
fileVersion
|
|
});
|
|
|
|
// uploadImageFileAWS
|
|
const fileAwsRes = await webApiService.execute({
|
|
url: fileStartRes.json.url,
|
|
uploadFilePUT: true,
|
|
fileData: base64File,
|
|
fileMIME: 'image/png',
|
|
fileMD5: fileMd5
|
|
});
|
|
if (fileAwsRes.status !== 200) {
|
|
$throw(
|
|
fileAwsRes.status,
|
|
`${type} image upload failed`,
|
|
fileStartRes.json.url
|
|
);
|
|
}
|
|
|
|
// imageFileFinish
|
|
await api.fileFinish({ fileId: uploadedFileId, fileVersion });
|
|
|
|
// imageSigStart
|
|
const sigStartRes = await api.sigStart({
|
|
fileId: uploadedFileId,
|
|
fileVersion
|
|
});
|
|
|
|
// uploadImageSigAWS
|
|
const sigAwsRes = await webApiService.execute({
|
|
url: sigStartRes.json.url,
|
|
uploadFilePUT: true,
|
|
fileData: base64SignatureFile,
|
|
fileMIME: 'application/x-rsync-signature',
|
|
fileMD5: signatureMd5
|
|
});
|
|
if (sigAwsRes.status !== 200) {
|
|
$throw(
|
|
sigAwsRes.status,
|
|
`${type} image upload failed`,
|
|
sigStartRes.json.url
|
|
);
|
|
}
|
|
|
|
// imageSigFinish
|
|
await api.sigFinish({ fileId: uploadedFileId, fileVersion });
|
|
|
|
// imageSet
|
|
const newImageUrl = `${AppDebug.endpointDomain}/file/${uploadedFileId}/${fileVersion}/file`;
|
|
const setRes = await api.setImage({ id: entityId, imageUrl: newImageUrl });
|
|
if (setRes.json.imageUrl !== newImageUrl) {
|
|
$throw(0, `${type} image change failed`, newImageUrl);
|
|
}
|
|
}
|