This commit is contained in:
Manav Rathi 2024-11-21 10:11:09 +05:30
parent 142a1bd684
commit 8505383827
No known key found for this signature in database
3 changed files with 40 additions and 21 deletions

View File

@ -5,7 +5,10 @@ import { ensureElectron } from "@/base/electron";
import { basename, nameAndExtension } from "@/base/file-name";
import log from "@/base/log";
import { CustomErrorMessage } from "@/base/types/ipc";
import { detectFileTypeInfoFromChunk } from "@/gallery/utils/detect-type";
import {
detectFileTypeInfoFromChunk,
isFileTypeNotSupportedError,
} from "@/gallery/utils/detect-type";
import { readStream } from "@/gallery/utils/native-stream";
import {
EncryptedMagicMetadata,
@ -539,8 +542,19 @@ export const uploader = async (
* (tee will not work for strictly sequential reads of large streams).
*/
const { fileTypeInfo, fileSize, lastModifiedMs } =
await readAssetDetails(uploadAsset);
let assetDetails: ReadAssetDetailsResult;
try {
assetDetails = await readAssetDetails(uploadAsset);
} catch (e) {
if (isFileTypeNotSupportedError(e)) {
log.error(`Not uploading ${fileName}`, e);
return { uploadResult: UPLOAD_RESULT.UNSUPPORTED };
}
throw e;
}
const { fileTypeInfo, fileSize, lastModifiedMs } = assetDetails;
const maxFileSize = 4 * 1024 * 1024 * 1024; /* 4 GB */
if (fileSize >= maxFileSize)
@ -637,8 +651,6 @@ export const uploader = async (
} catch (e) {
if (e.message == CustomError.UPLOAD_CANCELLED) {
log.info(`Upload for ${fileName} cancelled`);
} else if (e.message == CustomError.UNSUPPORTED_FILE_FORMAT) {
log.info(`Not uploading ${fileName}: unsupported file format`);
} else {
log.error(`Upload failed for ${fileName}`, e);
}
@ -647,8 +659,6 @@ export const uploader = async (
switch (error.message) {
case CustomError.ETAG_MISSING:
return { uploadResult: UPLOAD_RESULT.BLOCKED };
case CustomError.UNSUPPORTED_FILE_FORMAT:
return { uploadResult: UPLOAD_RESULT.UNSUPPORTED };
case CustomError.FILE_TOO_LARGE:
return {
uploadResult: UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE,

View File

@ -5,21 +5,14 @@ import {
KnownNonMediaFileExtensions,
type FileTypeInfo,
} from "@/media/file-type";
import { CustomError } from "@ente/shared/error";
import FileTypeDetect from "file-type";
/**
* Read the file's initial contents or use the file's name to detect its type.
*
* This function first reads an initial chunk of the file and tries to detect
* the file's {@link FileTypeInfo} from it. If that doesn't work, it then falls
* back to using the file's name to detect it.
*
* If neither of these two approaches work, it throws an exception.
*
* If we were able to detect the file type, but it is explicitly not a media
* (image or video) format that we support, this function throws an error with
* the message `CustomError.UNSUPPORTED_FILE_FORMAT`.
* This is a more convenient to use abstraction over
* {@link detectFileTypeInfoFromChunk} for use when we already have a
* {@link File} object. See that method's documentation for more details.
*
* @param file A {@link File} object
*
@ -37,7 +30,18 @@ export const detectFileTypeInfo = async (file: File): Promise<FileTypeInfo> =>
* However, this lower level function is also exposed for use in cases like
* during upload where we might not have a File object and would like to provide
* the initial chunk of the file's contents in a different way.
* This function first reads an initial chunk of the file and tries to detect
* the file's {@link FileTypeInfo} from it. If that doesn't work, it then falls
* back to using the file's name to detect it.
*
* If neither of these two approaches work, it throws an exception.
*
* If we were able to detect the file type, but it is explicitly not a media
* (image or video) format that we support, then also this function will throw
* an exception. Such exceptions can be identified using the
* {@link isFileTypeNotSupportedError} predicate.
*
* @param readInitialChunk A function to call to read the initial chunk of the
* file's data. There is no strict requirement for the size of the chunk this
* function should return, generally the first few KBs should be good.
@ -63,7 +67,8 @@ export const detectFileTypeInfoFromChunk = async (
} else if (mimeType.startsWith("video/")) {
fileType = FileType.video;
} else {
throw new Error(CustomError.UNSUPPORTED_FILE_FORMAT);
// This string should satisfy `isFileTypeNotSupportedError`.
throw new Error(`Unsupported file format (MIME type ${mimeType})`);
}
return {
@ -78,13 +83,18 @@ export const detectFileTypeInfoFromChunk = async (
const known = KnownFileTypeInfos.find((f) => f.extension == extension);
if (known) return known;
if (extension && KnownNonMediaFileExtensions.includes(extension))
throw Error(CustomError.UNSUPPORTED_FILE_FORMAT);
if (extension && KnownNonMediaFileExtensions.includes(extension)) {
// This string should satisfy `isFileTypeNotSupportedError`.
throw new Error(`Unsupported file format (extension ${extension})`);
}
throw e;
}
};
export const isFileTypeNotSupportedError = (e: unknown) =>
e instanceof Error && e.message.startsWith("Unsupported file format");
const readInitialChunkOfFile = async (file: File) => {
const chunkSizeForTypeDetection = 4100;
const chunk = file.slice(0, chunkSizeForTypeDetection);

View File

@ -24,7 +24,6 @@ export function isApiErrorResponse(object: any): object is ApiErrorResponse {
export const CustomError = {
ETAG_MISSING: "no header/etag present in response body",
KEY_MISSING: "encrypted key missing from localStorage",
UNSUPPORTED_FILE_FORMAT: "unsupported file format",
FILE_TOO_LARGE: "file too large",
SUBSCRIPTION_EXPIRED: "subscription expired",
STORAGE_QUOTA_EXCEEDED: "storage quota exceeded",