* feat: Allow data imports larger than the standard attachment size * Use correct preset for data imports * Cleanup of expired attachments * lint
90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import invariant from "invariant";
|
|
import { AttachmentPreset } from "@shared/types";
|
|
import { client } from "./ApiClient";
|
|
import Logger from "./Logger";
|
|
|
|
type UploadOptions = {
|
|
/** The user facing name of the file */
|
|
name?: string;
|
|
/** The document that this file was uploaded in, if any */
|
|
documentId?: string;
|
|
/** The preset to use for attachment configuration */
|
|
preset: AttachmentPreset;
|
|
/** Callback will be passed a number between 0-1 as upload progresses */
|
|
onProgress?: (fractionComplete: number) => void;
|
|
};
|
|
|
|
export const uploadFile = async (
|
|
file: File | Blob,
|
|
options: UploadOptions = {
|
|
name: "",
|
|
preset: AttachmentPreset.DocumentAttachment,
|
|
}
|
|
) => {
|
|
const name = file instanceof File ? file.name : options.name;
|
|
const response = await client.post("/attachments.create", {
|
|
preset: options.preset,
|
|
documentId: options.documentId,
|
|
contentType: file.type,
|
|
size: file.size,
|
|
name,
|
|
});
|
|
invariant(response, "Response should be available");
|
|
const data = response.data;
|
|
const attachment = data.attachment;
|
|
const formData = new FormData();
|
|
|
|
for (const key in data.form) {
|
|
formData.append(key, data.form[key]);
|
|
}
|
|
|
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'blob' does not exist on type 'File | Blo... Remove this comment to see the full error message
|
|
if (file.blob) {
|
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'file' does not exist on type 'File | Blo... Remove this comment to see the full error message
|
|
formData.append("file", file.file);
|
|
} else {
|
|
formData.append("file", file);
|
|
}
|
|
|
|
// Using XMLHttpRequest instead of fetch because fetch doesn't support progress
|
|
const xhr = new XMLHttpRequest();
|
|
const success = await new Promise((resolve) => {
|
|
xhr.upload.addEventListener("progress", (event) => {
|
|
if (event.lengthComputable && options.onProgress) {
|
|
options.onProgress(event.loaded / event.total);
|
|
}
|
|
});
|
|
xhr.addEventListener("error", () => {
|
|
Logger.error(
|
|
"File upload failed",
|
|
new Error(`${xhr.status} ${xhr.statusText}`)
|
|
);
|
|
});
|
|
xhr.addEventListener("loadend", () => {
|
|
resolve(xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 400);
|
|
});
|
|
xhr.open("POST", data.uploadUrl, true);
|
|
xhr.send(formData);
|
|
});
|
|
|
|
if (!success) {
|
|
throw new Error("Upload failed");
|
|
}
|
|
|
|
return attachment;
|
|
};
|
|
|
|
export const dataUrlToBlob = (dataURL: string) => {
|
|
const blobBin = atob(dataURL.split(",")[1]);
|
|
const array = [];
|
|
|
|
for (let i = 0; i < blobBin.length; i++) {
|
|
array.push(blobBin.charCodeAt(i));
|
|
}
|
|
|
|
const file = new Blob([new Uint8Array(array)], {
|
|
type: "image/png",
|
|
});
|
|
return file;
|
|
};
|