From 0ba58a1efb2527d121034160a673e342dbf39223 Mon Sep 17 00:00:00 2001 From: shb Date: Tue, 17 Jun 2025 13:22:12 +0800 Subject: [PATCH] Testing path for uploading files Test route and test file uploading functionality added and confirmed to run properly. Utilized direct-to-storage upload method to bypass sending file data to backend --- package-lock.json | 33 +++++++++++++++++++++ package.json | 1 + pages/test.vue | 25 +++++++++++++++- server/api/test/test-response.post.js | 41 +++++++++++++++++++++------ yarn.lock | 24 ++++++++++++++++ 5 files changed, 114 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a29f29..5d2b628 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "hasInstallScript": true, "dependencies": { "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.830.0", "@babel/eslint-parser": "^7.19.1", "@codemirror/lang-html": "^6.4.3", "@codemirror/lang-javascript": "^6.1.6", @@ -818,6 +819,24 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.830.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.830.0.tgz", + "integrity": "sha512-IEiSJfuf/hcf9WvCmvV35ci1yGcV3IYKS0e6l5xPRLYDNBrAWao9j8mrp0N4WCD3Nr+3xZRY5JglEFAH6CN3OQ==", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.826.0", + "@aws-sdk/types": "3.821.0", + "@aws-sdk/util-format-url": "3.821.0", + "@smithy/middleware-endpoint": "^4.1.11", + "@smithy/protocol-http": "^5.1.2", + "@smithy/smithy-client": "^4.4.3", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/signature-v4-multi-region": { "version": "3.826.0", "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz", @@ -888,6 +907,20 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.821.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz", + "integrity": "sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg==", + "dependencies": { + "@aws-sdk/types": "3.821.0", + "@smithy/querystring-builder": "^4.0.4", + "@smithy/types": "^4.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.804.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", diff --git a/package.json b/package.json index bd83ae8..39cf09a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.828.0", + "@aws-sdk/s3-request-presigner": "^3.830.0", "@babel/eslint-parser": "^7.19.1", "@codemirror/lang-html": "^6.4.3", "@codemirror/lang-javascript": "^6.1.6", diff --git a/pages/test.vue b/pages/test.vue index 64e8fc8..507b8f2 100644 --- a/pages/test.vue +++ b/pages/test.vue @@ -32,8 +32,10 @@ async function handleSubmit() { multipartFormData.append('name', formData.value.name); multipartFormData.append('animal', formData.value.animal); + // Only send file metadata if a file is selected if (selectedFile.value) { - multipartFormData.append('file', selectedFile.value); + multipartFormData.append('fileName', selectedFile.value.name); + multipartFormData.append('fileType', selectedFile.value.type); } const response = await fetch('/api/test/test-response', { @@ -43,6 +45,27 @@ async function handleSubmit() { const data = await response.json(); message.value = data.message; + + // If we have a file and received a signed URL, upload to S3 + if (selectedFile.value && data.signedUrl) { + try { + const s3Response = await fetch(data.signedUrl, { + method: 'PUT', + body: selectedFile.value, + headers: { + 'Content-Type': selectedFile.value.type + } + }); + + if (!s3Response.ok) { + throw new Error('Failed to upload file to S3'); + } + + message.value += '\nFile successfully uploaded to S3!'; + } catch (s3Error) { + message.value += '\nError uploading file to S3: ' + s3Error.message; + } + } } catch (error) { message.value = 'Error: ' + error.message; } diff --git a/server/api/test/test-response.post.js b/server/api/test/test-response.post.js index dbced6f..af4dba1 100644 --- a/server/api/test/test-response.post.js +++ b/server/api/test/test-response.post.js @@ -1,8 +1,18 @@ import { readMultipartFormData } from 'h3'; +import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; export default defineEventHandler(async (event) => { + const s3 = new S3Client({ + region: 'ap-southeast-1', + credentials: { + accessKeyId: process.env.NUXT_AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.NUXT_AWS_SECRET_ACCESS_KEY, + } + }); + try { - const parts = await readMultipartFormData(event, { maxSize: 10 * 1024 * 1024 }); // 10MB limit + const parts = await readMultipartFormData(event, { maxSize: 1024 * 1024 }); // 1MB limit since we're only handling metadata if (!parts) { return { @@ -14,24 +24,37 @@ export default defineEventHandler(async (event) => { // Extract form data const name = parts.find(p => p.name === 'name')?.data.toString() || ''; const animal = parts.find(p => p.name === 'animal')?.data.toString() || ''; - const file = parts.find(p => p.name === 'file'); + const fileName = parts.find(p => p.name === 'fileName')?.data.toString(); + const fileType = parts.find(p => p.name === 'fileType')?.data.toString(); // Log the received data console.log('Received name:', name); console.log('Received favorite animal:', animal); - if (file) { - console.log('Received file:', file.filename); - } - + // Prepare response message let message = `You are ${name} and your favorite animal is ${animal}`; - if (file) { - message += `. You uploaded the file: ${file.filename}`; + + let signedUrl = null; + if (fileName && fileType) { + console.log('Received file metadata:', { fileName, fileType }); + message += `. You will upload the file: ${fileName}`; + + // Generate a unique key using timestamp and filename + const uploadKey = `uploads/${Date.now()}-${fileName}`; + + const s3Command = new PutObjectCommand({ + Bucket: process.env.NUXT_AWS_BUCKET, + Key: uploadKey, + ContentType: fileType, + }); + + signedUrl = await getSignedUrl(s3, s3Command, { expiresIn: 60 }); } return { status: 200, - message + message, + signedUrl }; } catch (error) { console.error("Error processing request:", error); diff --git a/yarn.lock b/yarn.lock index 19a31fd..b33cd8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -504,6 +504,20 @@ "@smithy/util-middleware" "^4.0.4" tslib "^2.6.2" +"@aws-sdk/s3-request-presigner@^3.830.0": + version "3.830.0" + resolved "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.830.0.tgz" + integrity sha512-IEiSJfuf/hcf9WvCmvV35ci1yGcV3IYKS0e6l5xPRLYDNBrAWao9j8mrp0N4WCD3Nr+3xZRY5JglEFAH6CN3OQ== + dependencies: + "@aws-sdk/signature-v4-multi-region" "3.826.0" + "@aws-sdk/types" "3.821.0" + "@aws-sdk/util-format-url" "3.821.0" + "@smithy/middleware-endpoint" "^4.1.11" + "@smithy/protocol-http" "^5.1.2" + "@smithy/smithy-client" "^4.4.3" + "@smithy/types" "^4.3.1" + tslib "^2.6.2" + "@aws-sdk/signature-v4-multi-region@3.826.0": version "3.826.0" resolved "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.826.0.tgz" @@ -554,6 +568,16 @@ "@smithy/util-endpoints" "^3.0.6" tslib "^2.6.2" +"@aws-sdk/util-format-url@3.821.0": + version "3.821.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.821.0.tgz" + integrity sha512-h+xqmPToxDrZ0a7rxE1a8Oh4zpWfZe9oiQUphGtfiGFA6j75UiURH5J3MmGHa/G4t15I3iLLbYtUXxvb1i7evg== + dependencies: + "@aws-sdk/types" "3.821.0" + "@smithy/querystring-builder" "^4.0.4" + "@smithy/types" "^4.3.1" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.804.0" resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz"