diff --git a/README.md b/README.md index 993d309..79cc4d7 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,23 @@ Response: { } ``` +#### Download File + +```http +GET /files/download?file_id=1 +# or +GET /files/download?file_path=path/to/file.txt +# or +GET /files/download/path/to/file.txt + +Response: raw file contents with a Content-Disposition attachment header. +``` + +Use the download endpoints when you want a browser-friendly attachment URL +instead of the JSON metadata returned by `/files/get`. Text files are returned as +`text/plain`; binary files are decoded from `binary_content_b64` and returned as +`application/octet-stream` with a `Content-Length` header. + #### List Files ```http diff --git a/routes/index.ts b/routes/index.ts index ade1972..b3bc6a9 100644 --- a/routes/index.ts +++ b/routes/index.ts @@ -13,6 +13,8 @@ This is a simple file server API, it has the following API: /health - Health check /files/get?file_path=... - Get a file +/files/download?file_path=... - Download a file as an attachment +/files/download/path/to/file.txt - Download a file path as an attachment /files/list - List all files /files/upsert - Upsert a file diff --git a/tests/routes/files.test.ts b/tests/routes/files.test.ts index 196c354..8ba36e4 100644 --- a/tests/routes/files.test.ts +++ b/tests/routes/files.test.ts @@ -65,7 +65,7 @@ test("binary file operations", async () => { test("file download operations", async () => { const { axios } = await getTestServer() - await axios.post("/files/upsert", { + const createRes = await axios.post("/files/upsert", { file_path: "/download-test.txt", text_content: "Test download content", }) @@ -80,6 +80,15 @@ test("file download operations", async () => { 'attachment; filename="download-test.txt"', ) + const downloadByIdRes = await axios.get("/files/download", { + params: { file_id: createRes.data.file.file_id }, + }) + expect(downloadByIdRes.status).toBe(200) + expect(downloadByIdRes.data).toBe("Test download content") + expect(downloadByIdRes.headers.get("content-disposition")).toBe( + 'attachment; filename="download-test.txt"', + ) + expect( axios.get("/files/download", { params: { file_path: "/missing-file.txt" }, @@ -112,6 +121,25 @@ test("file download operations2", async () => { }) }) +test("nested file download path preserves subdirectories", async () => { + const { axios } = await getTestServer() + + await axios.post("/files/upsert", { + file_path: "/nested/path/download-test3.txt", + text_content: "Nested download content", + }) + + const successRes = await axios.get( + "/files/download/nested/path/download-test3.txt", + ) + expect(successRes.status).toBe(200) + expect(successRes.data).toBe("Nested download content") + expect(successRes.headers.get("content-type")).toBe("text/plain") + expect(successRes.headers.get("content-disposition")).toBe( + 'attachment; filename="download-test3.txt"', + ) +}) + test("file delete operations", async () => { const { axios } = await getTestServer()