You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS+TRPC文件上传报错求助:404与格式匹配错误

解决NextJS + TRPC 文件上传的404与格式匹配错误

问题场景

使用NextJS结合TRPC开发Web应用,在标注"use client"的页面中有文件选择表单,希望通过TRPC将文件传递到后端处理,但调用时出现两个错误:

  1. 404 Not Found(uploadFile接口)
  2. TRPCClientError: The string did not match the expected pattern

用户提供的代码

客户端代码

"use client"

import * as z from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"
import { Button } from "../ui/button"
import { Input } from "../ui/input"
import { trpc } from "@/app/_trpc/client"

const TrainForm = () => {

  const formSchema = z.object({
    file: z.instanceof(File)
  })

  const { mutate: uploadFile, isLoading } = trpc.uploadFile.useMutation()

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema)
  })

  function onSubmit(data: z.infer<typeof formSchema>) {
    console.log(data.file)

    // Handle file
uploadFile({
  file: data.file
})
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className=" space-y-4 p-8">

        <FormField
          control={form.control}
          name="file"
          render={({ field }) => (
            <Input accept="application/pdf" type="file" name="file" onChange={(e: any) => {
              const file = e.target.files?.[0]
              field.onChange(file)
            }} />
          )}
        />

        <Button disabled={isLoading} type="submit">
          Submit
        </Button>
      </form>
    </Form>
  );
}

export default TrainForm;

TRPC路由代码

import { privateProcedure, publicProcedure, router } from './trpc';
import { TRPCError } from '@trpc/server'
import { db } from '@/db';
import { z } from 'zod';

export const appRouter = router({
    // query to get data
    // mutation to modify data


    // Private
    uploadFile: publicProcedure.input(z.object({
      file: z.instanceof(File)
    })).mutation(async ({input}) => {

      // handle file
    }),

  });

// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;

错误信息

[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (uploadFile, line 0)
[Error] TRPCClientError: The string did not match the expected pattern.
    (anonymous function) (app-index.js:32)
    (anonymous function) (hydration-error-info.js:41)
    (anonymous function) (mutation.mjs:167)

问题根源

  1. 404错误:TRPC的API路由配置缺失或路径不匹配,导致客户端无法找到对应的后端接口。
  2. 格式匹配错误:TRPC默认使用JSON传输数据,但浏览器的File对象无法被JSON序列化;同时后端Node环境中不存在浏览器的File类,z.instanceof(File)的验证在后端完全无效。

解决方案

步骤1:修复TRPC API路由配置

确保在app/api/trpc/[trpc]/route.ts文件中正确配置TRPC的请求处理器,文件内容如下:

import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/trpc/router'; // 路径根据你的项目结构调整

const handler = (req: Request) =>
  fetchRequestHandler({
    endpoint: '/api/trpc',
    req,
    router: appRouter,
    createContext: () => ({}), // 若无上下文需求,返回空对象即可
  });

export { handler as GET, handler as POST };

步骤2:调整客户端文件传输方式

TRPC无法直接通过JSON传输File对象,需改用FormData包装文件。修改客户端的onSubmit函数:

function onSubmit(data: z.infer<typeof formSchema>) {
  const formData = new FormData();
  formData.append('file', data.file);
  uploadFile(formData);
}

步骤3:修正TRPC后端的输入验证

后端Node环境无法识别浏览器的File类,需改用FormData解析并验证文件。更新TRPC路由代码:

import { privateProcedure, publicProcedure, router } from './trpc';
import { TRPCError } from '@trpc/server';
import { db } from '@/db';
import { z } from 'zod';
import { parseFormData } from '@trpc/server/adapters/fetch';

export const appRouter = router({
  uploadFile: publicProcedure
    // 手动解析并验证FormData
    .input(async (opts) => {
      const formData = await parseFormData(opts.req);
      const file = formData.get('file');
      
      if (!file || typeof file === 'string') {
        throw new TRPCError({ code: 'BAD_REQUEST', message: '请上传有效的PDF文件' });
      }
      
      // 可选:验证文件类型
      if (!file.type.includes('application/pdf')) {
        throw new TRPCError({ code: 'BAD_REQUEST', message: '仅支持PDF格式文件' });
      }

      return { file };
    })
    .mutation(async ({ input }) => {
      // 这里可以处理文件,比如读取为Buffer、上传到存储服务等
      console.log('收到文件:', input.file.name, input.file.size);
      return { success: true, filename: input.file.name };
    }),
});

export type AppRouter = typeof appRouter;

步骤4:确认客户端TRPC配置路径

确保客户端TRPC客户端的请求URL与API路由一致,比如在app/_trpc/client.ts中:

import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/trpc/router';
import { httpBatchLink } from '@trpc/client';

export const trpc = createTRPCReact<AppRouter>();

export const createTRPCClient = () => {
  return trpc.createClient({
    links: [
      httpBatchLink({
        url: '/api/trpc', // 必须与API路由的endpoint一致
      }),
    ],
  });
};

验证

完成上述修改后,重新启动项目:

  1. 选择PDF文件并提交,控制台应打印文件信息,无404错误
  2. 后端可以正常接收并处理文件

内容的提问来源于stack exchange,提问作者Santiago Padilla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:26:05