mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-15 14:28:45 +00:00
파일을 업로드할 때 스트리밍이 되지 않고 버퍼링하던 버그 수정
This commit is contained in:
@@ -50,6 +50,7 @@
|
||||
"vite": "^5.4.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^3.1.1",
|
||||
"argon2": "^0.41.1",
|
||||
"better-sqlite3": "^11.7.2",
|
||||
"drizzle-orm": "^0.33.0",
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@fastify/busboy':
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1
|
||||
argon2:
|
||||
specifier: ^0.41.1
|
||||
version: 0.41.1
|
||||
@@ -602,6 +605,9 @@ packages:
|
||||
resolution: {integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@fastify/busboy@3.1.1':
|
||||
resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@@ -2543,6 +2549,8 @@ snapshots:
|
||||
dependencies:
|
||||
levn: 0.4.1
|
||||
|
||||
'@fastify/busboy@3.1.1': {}
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
|
||||
@@ -2,7 +2,8 @@ import { error } from "@sveltejs/kit";
|
||||
import { createReadStream, createWriteStream } from "fs";
|
||||
import { mkdir, stat, unlink } from "fs/promises";
|
||||
import { dirname } from "path";
|
||||
import { Readable, Writable } from "stream";
|
||||
import { Readable } from "stream";
|
||||
import { pipeline } from "stream/promises";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { IntegrityError } from "$lib/server/db/error";
|
||||
import {
|
||||
@@ -94,7 +95,7 @@ const safeUnlink = async (path: string) => {
|
||||
|
||||
export const uploadFile = async (
|
||||
params: Omit<NewFileParams, "path">,
|
||||
encContentStream: ReadableStream<Uint8Array>,
|
||||
encContentStream: Readable,
|
||||
) => {
|
||||
const oneMinuteAgo = new Date(Date.now() - 60 * 1000);
|
||||
const oneMinuteLater = new Date(Date.now() + 60 * 1000);
|
||||
@@ -106,9 +107,7 @@ export const uploadFile = async (
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
|
||||
try {
|
||||
await encContentStream.pipeTo(
|
||||
Writable.toWeb(createWriteStream(path, { flags: "wx", mode: 0o600 })),
|
||||
);
|
||||
await pipeline(encContentStream, createWriteStream(path, { flags: "wx", mode: 0o600 }));
|
||||
await registerFile({
|
||||
...params,
|
||||
path,
|
||||
|
||||
@@ -60,9 +60,16 @@
|
||||
data.id,
|
||||
$masterKeyStore?.get(1)!,
|
||||
$hmacSecretStore?.get(1)!,
|
||||
).then(() => {
|
||||
info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME
|
||||
});
|
||||
)
|
||||
.then(() => {
|
||||
// TODO: FIXME
|
||||
info = getDirectoryInfo(data.id, $masterKeyStore?.get(1)?.key!);
|
||||
window.alert("파일이 업로드되었어요.");
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: FIXME
|
||||
window.alert("파일 업로드에 실패했어요.");
|
||||
});
|
||||
};
|
||||
|
||||
const loadAndUploadFile = async () => {
|
||||
|
||||
@@ -1,19 +1,57 @@
|
||||
import Busboy from "@fastify/busboy";
|
||||
import { error, text } from "@sveltejs/kit";
|
||||
import { Readable, Writable } from "stream";
|
||||
import { authorize } from "$lib/server/modules/auth";
|
||||
import { fileUploadRequest } from "$lib/server/schemas";
|
||||
import { uploadFile } from "$lib/server/services/file";
|
||||
import type { RequestHandler } from "./$types";
|
||||
|
||||
const parseFormData = async (contentType: string, body: ReadableStream<Uint8Array>) => {
|
||||
return new Promise<{ metadata: string; content: Readable }>((resolve, reject) => {
|
||||
let metadata: string | null = null;
|
||||
let content: Readable | null = null;
|
||||
|
||||
const bb = Busboy({ headers: { "content-type": contentType } });
|
||||
bb.on("field", (fieldname, val) => {
|
||||
if (fieldname !== "metadata") return reject(new Error("Invalid request body"));
|
||||
if (metadata || content) return reject(new Error("Invalid request body")); // metadata must be first
|
||||
metadata = val;
|
||||
});
|
||||
bb.on("file", (fieldname, file) => {
|
||||
if (fieldname !== "content") return reject(new Error("Invalid request body"));
|
||||
if (!metadata || content) return reject(new Error("Invalid request body")); // metadata must be first
|
||||
content = file;
|
||||
resolve({ metadata, content });
|
||||
});
|
||||
bb.on("finish", () => reject(new Error("Invalid request body")));
|
||||
bb.on("error", (e) => reject(e));
|
||||
|
||||
body.pipeTo(Writable.toWeb(bb));
|
||||
});
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ locals, request }) => {
|
||||
const { userId } = await authorize(locals, "activeClient");
|
||||
|
||||
const form = await request.formData();
|
||||
const metadata = form.get("metadata");
|
||||
const content = form.get("content");
|
||||
if (typeof metadata !== "string" || !(content instanceof File)) {
|
||||
const contentTypeHeader = request.headers.get("Content-Type");
|
||||
if (!contentTypeHeader?.startsWith("multipart/form-data") || !request.body) {
|
||||
error(400, "Invalid request body");
|
||||
}
|
||||
|
||||
let metadata;
|
||||
let content;
|
||||
|
||||
try {
|
||||
const formData = await parseFormData(contentTypeHeader, request.body);
|
||||
metadata = formData.metadata;
|
||||
content = formData.content;
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message === "Invalid request body") {
|
||||
error(400, "Invalid request body");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
const zodRes = fileUploadRequest.safeParse(JSON.parse(metadata));
|
||||
if (!zodRes.success) error(400, "Invalid request body");
|
||||
const {
|
||||
@@ -53,7 +91,7 @@ export const POST: RequestHandler = async ({ locals, request }) => {
|
||||
encLastModifiedAt: lastModifiedAt,
|
||||
encLastModifiedAtIv: lastModifiedAtIv,
|
||||
},
|
||||
content.stream(),
|
||||
content,
|
||||
);
|
||||
return text("File uploaded", { headers: { "Content-Type": "text/plain" } });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user