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

Prisma与Supabase多图上传问题:imageUrl类型不兼容报错排查

多图上传至Supabase后,Prisma写入数据库报imageUrl类型不兼容错误

问题描述

我有一个支持多图上传的Input组件,已成功将图片上传至Supabase存储,但尝试将图片URL写入Prisma数据库时失败,执行create操作时提示**"Types of property 'imageUrl' are incompatible"**类型不兼容错误。

相关代码

图片输入组件代码

<Input name="image" type="file" multiple={true} required />

服务端操作代码

export async function createDescription(formData: FormData) {

  const homeId = formData.get("homeId") as string;
  const photos = formData.getAll("image") as string[];

  const photoNames = photos.map((_, index) => `blog-${Date.now()}-${index}`);

  
  const uploadPromises = photos.map((photo, index) => {
    return supabase.storage.from("images").upload(photoNames[index], photo, {
      contentType: "image/png",
      cacheControl: "2592000",
    });
  });

  const uploadResults = await Promise.all(uploadPromises);
  const errors = uploadResults.map((result) => result.error).filter(Boolean);

  if (errors.length > 0) {
    return { error: "Unable to upload one or more blog images to Storage." };
  }

  const paths = uploadResults.map((result) => result.data?.path);
  const photoUrls = paths.map(
    (path) =>
      `${process.env.SUPABASE_URL}/storage/v1/object/public/images/${path}`,
  );

  const { data: imageData } = await supabase.from("blogs").update([
    {
      homeId,
      photoUrls,
    },
  ]);

  if (imageData === null) {
    return { error: "Unable to upload one or more blog images to Storage." };
  }

  const imageFilesUrl = photoUrls.map((url) => ({ url }));

  const data = await prisma.home.update({
    where: {
      id: homeId,
    },
    data: {
      title,
      addedDescription: true,

      HomePhoto: {
        create: {
          imageUrl: imageFiles,
        },
      },
    },
  });

  return redirect(`/create/${homeId}/address`);
}

Prisma模型代码

model User {
  id           String  @id @unique
  authId       String  @unique
  email        String  @unique
  firstName    String
  lastName     String
  profileImage String?
  Home         Home[]
}

model Home {
  id           String  @id @default(uuid())
  title        String?
  description  String?
  guests       String?
  bedrooms     String?
  bathrooms    String?
  country      String?
  price        Int?
  categoryName String?

  addedCategory    Boolean @default(false)
  addedDescription Boolean @default(false)
  addedLocation    Boolean @default(false)

  createdAT DateTime @default(now())
  User      User?    @relation(fields: [userId], references: [id])
  userId    String?
  HomePhoto Photo[]
}

model Photo {
  id       String @id @default(uuid())
  imageUrl String

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  Home   Home   @relation(fields: [homeId], references: [id])
  homeId String
}

错误原因及修复方案

1. File类型断言错误

formData.getAll("image")获取的是File对象数组,不是字符串数组,类型断言as string[]会导致后续上传逻辑异常,同时引发类型不匹配。

修复:

const photos = formData.getAll("image") as File[];

2. Prisma关联创建格式错误

  • Home模型的HomePhoto是Photo[]数组,创建关联时需要传入对象数组,而非单个对象。
  • 代码中imageFiles未定义,实际应该用photoUrls直接生成符合Photo模型的对象数组,且imageUrl需要是字符串,不是对象。

修复:

// 生成符合Photo模型的对象数组
const photoRecords = photoUrls.filter(url => url !== undefined).map(url => ({
  imageUrl: url
}));

const data = await prisma.home.update({
  where: {
    id: homeId,
  },
  data: {
    title,
    addedDescription: true,
    // 传入数组创建多条Photo记录
    HomePhoto: {
      create: photoRecords,
    },
  },
});

3. 处理路径为null的情况

uploadResults.map((result) => result.data?.path)可能产生null值,生成URL时需要过滤,避免无效URL写入数据库:

修复:

const photoUrls = paths
  .filter(path => path !== undefined) // 过滤null/undefined路径
  .map(path => `${process.env.SUPABASE_URL}/storage/v1/object/public/images/${path}`);

4. 清理冗余代码

代码中更新Supabase的blogs表属于冗余操作,和Prisma写入逻辑无关,可以直接删除:

// 删除这段冗余代码
const { data: imageData } = await supabase.from("blogs").update([
  {
    homeId,
    photoUrls,
  },
]);

if (imageData === null) {
  return { error: "Unable to upload one or more blog images to Storage." };
}

修复后的完整服务端代码示例

export async function createDescription(formData: FormData) {
  const homeId = formData.get("homeId") as string;
  const photos = formData.getAll("image") as File[];
  const title = formData.get("title") as string; // 补充原代码中未定义的title获取逻辑

  const photoNames = photos.map((_, index) => `blog-${Date.now()}-${index}`);

  const uploadPromises = photos.map((photo, index) => {
    return supabase.storage.from("images").upload(photoNames[index], photo, {
      contentType: photo.type, // 用File本身的类型,而非硬编码png
      cacheControl: "2592000",
    });
  });

  const uploadResults = await Promise.all(uploadPromises);
  const errors = uploadResults.map((result) => result.error).filter(Boolean);

  if (errors.length > 0) {
    return { error: "部分图片上传至存储失败" };
  }

  const paths = uploadResults.map((result) => result.data?.path);
  const photoUrls = paths
    .filter(path => path !== undefined)
    .map(path => `${process.env.SUPABASE_URL}/storage/v1/object/public/images/${path}`);

  const photoRecords = photoUrls.map(url => ({
    imageUrl: url
  }));

  try {
    await prisma.home.update({
      where: { id: homeId },
      data: {
        title,
        addedDescription: true,
        HomePhoto: { create: photoRecords }
      }
    });
  } catch (dbError) {
    console.error("数据库写入失败:", dbError);
    return { error: "图片信息写入数据库失败" };
  }

  return redirect(`/create/${homeId}/address`);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:44:52