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-error
export 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 paths
export 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)(.*)',
],
};