ryanto / next-s3-upload

Upload files from your Next.js app to S3
https://next-s3-upload.codingvalue.com/
543 stars 76 forks source link

Custom Path for File Uploads with App Router #176

Open umairayub79 opened 5 months ago

umairayub79 commented 5 months ago

I am encountering difficulties specifying a custom path for file uploads when using the 'next-s3-upload' library within a Next.js application, specifically with the App Router. The library provides an example for configuring the API route with a custom key function, but it seems that this configuration may not be fully compatible with the App Router.

Documentation Reference: The relevant documentation snippet is as follows:

// pages/api/s3-upload.js
import { APIRoute } from "next-s3-upload";

export default APIRoute.configure({
  key(req, filename) {
    return `my/uploads/path/${filename}`;
  }
});

Issue Details:

Expected Behavior: I expect to be able to specify a custom path for file uploads using the library when using the App Router. Actual Behavior: When using the provided configuration with App Router, I encounter errors such as 'TypeError: Body is unusable' or 'TypeError: s.status is not a function'.

Additional Information:

The configuration appears to be designed for the Pages Router, and it may not work seamlessly with the App Router in Next.js. It seems that there might be a need for additional documentation or clarification on how to configure the library for the App Router in Next.js.

umairayub79 commented 5 months ago

Update

By using the POST export from "next-s3-upload/route" instead of APIRoute, I was able to specify a custom path for file uploads within the App Router.

import { POST as route } from "next-s3-upload/route";

export const POST = route.configure({
  async key(req, filename) {
    console.log('KEY CHANGED');
    return `${'myfiles'}/${filename}`;
  },
});

However, upon further testing, I encountered difficulties accessing the body of the req object when attempting to dynamically set the file path for uploads. The attempt to use await req.json() resulted in the error: TypeError: Body is unusable.

import { POST as route } from "next-s3-upload/route";

export const POST = route.configure({
  async key(req, filename) {
    try {
      const body = await req.json();
      const folderName = body.folderName;
      return `${folderName}/${filename}`;
    } catch (error) {
      console.error("Error parsing JSON body:", error);
      return `${filename}`;
    }
  },
});

Additionally, attempting to access the body directly using req.body returned: "ReadableStream { locked: true, state: 'closed', supportsBYOB: false }".

This further complicates the process of dynamically setting the file path.

GavOutdoors commented 4 months ago

I had the same issue. The request object is not usable in this context. To get around this, the client side posts the folder location in the header instead of the body.

const location = req.headers.get('location')

It now works as expected.

aliy321 commented 4 months ago

I cant seem to get body or headers to work but what i did to simplify things is use the url

let handleFileChange = async (event) => {
        let file = event.target.files[0];
        let { url } = await uploadToS3(file, {
            endpoint: {
                request: {
                    url: `/api/s3-upload/?folder=works`
                }
            }
        });

        setImageUrl(url);
    };
import { sanitizeKey } from "next-s3-upload";
import { POST as route } from "next-s3-upload/route";

export const POST = route.configure({
    async key(req, filename) {
        try {
            const url = req.url.split('?')[1];
            const params = new URLSearchParams(url);
            const folder = params.get('folder');

            return `${folder}/${sanitizeKey(filename)}`;
        } catch (error) {
            console.error("Error parsing JSON body:", error);
            return `${filename}`;
        }
    },
});