Hello, when I push app to Vercel and upload the file I got an error. But if I use localhost url to upload the file - it works well
Server logs:
⨯ TypeError: Failed to parse body as FormData. at node:internal/deps/undici/undici:4249:27 at successSteps (node:internal/deps/undici/undici:4288:27) at fullyReadBody (node:internal/deps/undici/undici:2724:9) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async consumeBody (node:internal/deps/undici/undici:4297:7) at async v (/var/task/.next/server/app/api/upload/route.js:6:9774) at async /var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:42484 at async eI.execute (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:32486) at async eI.handle (/var/task/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:43737) at async Y (/var/task/node_modules/next/dist/compiled/next-server/server.runtime.prod.js:16:24600)page.tsx:
import { Card } from '@/components/ui/card';import { handleSubmit } from '@/actions';import { Input } from '@/components/ui/input';import { Button } from '@/components/ui/button';
const Page = () => { return ( <Card className='p-3'> <form action={handleSubmit}> <Input type='file' name='files' /> <Button type='submit' className='mt-10'> Submit </Button> </form> </Card> );};
export default Page;server action:
'use server';
export const handleSubmit = async (e: FormData) => { const response = await fetch('https://dvzh.tech/api/upload', { method: 'POST', body: e, headers: { 'X-Upload-Folder': 'test', }, });
const data = await response.json();
console.log('data', data);};src/app/api/upload/route.ts:
import { NextRequest, NextResponse } from 'next/server';import { put } from '@vercel/blob';import { nanoid } from 'nanoid';
export async function POST(request: NextRequest) { const folder = request.headers.get('X-Upload-Folder') || 'any'; const uniqueName = nanoid(32);
const form = await request.formData(); const file = form.get('files') as File; const extension = file.name.split('.').pop(); const fileName = `${uniqueName}.${extension}`;
const blob = await put(`${folder}/${fileName}`, file, { access: 'public', });
return NextResponse.json(blob, { status: 201 });
// return await uploadFile(request, folder!);}also middleware:
import authConfig from '@/auth.config';import NextAuth from 'next-auth';import { API_ROUTES, authRoutes, DEFAULT_REDIRECT, publicApiRoutes, publicRoutes,} from '@/routes';import { NextResponse } from 'next/server';export const { auth } = NextAuth(authConfig);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment// @ts-expect-errorexport default auth((req) => { const { nextUrl } = req; const isLoggedIn = !!req.auth;
const requestHeaders = new Headers(req.headers); requestHeaders.set('x-url', nextUrl.pathname); requestHeaders.set('x-search', nextUrl.search);
const isApiAuthRoute = nextUrl.pathname.startsWith(API_ROUTES.AUTH); const isPublicRoute = publicRoutes.includes(nextUrl.pathname); const isAuthRoute = authRoutes.includes(nextUrl.pathname); const isPublicApiRoute = (publicApiRoutes as string[]).includes( nextUrl.pathname, );
// Allow public API routes if (isPublicApiRoute || isApiAuthRoute) { return null; }
// Authentication pages if (isAuthRoute) { // The way to create an absolute url if (isLoggedIn) { return Response.redirect(new URL(DEFAULT_REDIRECT, nextUrl)); }
return null; }
if (!isLoggedIn && !isPublicRoute) { // Redirect to previous page after login let callbackUrl = nextUrl.pathname;
if (nextUrl.search) { callbackUrl += nextUrl.search; }
const encodedCallbackUrl = encodeURIComponent(callbackUrl);
return Response.redirect( new URL(`/auth/login?callbackUrl=${encodedCallbackUrl}`, nextUrl), ); }
return NextResponse.next({ request: { headers: requestHeaders, }, });});
// Optionally, don't invoke Middleware on some pathsexport const config = { matcher: [ // Exclude files with a "." followed by an extension, which are typically static files. // Exclude files in the _next directory, which are Next.js internals.
'/((?!.+\\.[\\w]+$|_next).*)', // Re-include any files in the api or trpc folders that might have an extension '/(api|trpc)(.*)', ],};