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

如何通过HTML上传图片至PostgreSQL数据库(NestJS后端)

问题解决:File Input无法用[(ngModel)]绑定及图片路径存储方案

问题根源

file类型的<input>无法直接通过[(ngModel)]绑定数据,原因有两点:

  1. ngModel仅针对普通表单值(字符串、数字等),而file input返回的是File对象,不属于ngModel的处理范畴;
  2. 浏览器出于安全限制,不会暴露本地文件的真实路径,你无法直接获取本地路径字符串存入数据库。

解决方案

需要通过「前端用FormData传递文件+后端接收并存储文件,再将文件路径存入数据库」的流程实现需求,以下是具体代码修改:


前端(Angular)修改

  1. 调整Input标签,移除[(ngModel)],改用事件监听获取文件:
<input type="file" accept=".png, .jpeg, .jpg" (change)="onFileSelected($event)" class="form-control mt-3">
  1. 在food.component.ts中添加文件处理逻辑,用FormData封装数据:
selectedFile: File | null = null;

// 监听文件选择事件
onFileSelected(event: Event) {
  const input = event.target as HTMLInputElement;
  if (input.files?.length) {
    this.selectedFile = input.files[0];
  }
}

postFood() {
  if (this.sale === false) this.saleVal = 0;
  if (this.saleVal === 0) this.sale = false;

  // 用FormData封装表单数据和文件
  const formData = new FormData();
  formData.append('name', this.name);
  formData.append('price', this.price.toString());
  formData.append('description', this.description);
  formData.append('category', this.category);
  formData.append('sale', this.sale.toString());
  formData.append('genre', this.genre);
  formData.append('saleval', this.saleVal.toString());
  this.selectedFile && formData.append('image', this.selectedFile);

  // 发起请求
  this.productsService.postFood(formData).subscribe({
    next: () => console.log('产品添加成功'),
    error: err => console.error('添加失败:', err)
  });
}
  1. 修改products.service.ts的请求方法:
postFood(formData: FormData) {
  console.log('准备上传产品数据');
  return this.http.post('http://localhost:3000/food', formData);
}

后端(NestJS)修改

  1. 先安装文件上传依赖:
npm install @nestjs/platform-express multer
  1. 修改food.controller.ts,配置文件接收和存储:
import { Controller, Post, Body, UploadedFile, UseInterceptors, FileInterceptor } from '@nestjs/common';
import { diskStorage } from 'multer';
import { extname } from 'path';

@Controller('food')
export class FoodController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  @UseInterceptors(FileInterceptor('image', {
    storage: diskStorage({
      destination: './uploads', // 手动创建此目录用于存储上传的图片
      filename: (req, file, cb) => {
        // 生成唯一文件名避免冲突
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, `${file.fieldname}-${uniqueSuffix}${extname(file.originalname)}`);
      },
    }),
    // 限制文件类型
    fileFilter: (req, file, cb) => {
      const allowedExts = ['.png', '.jpeg', '.jpg'];
      const ext = extname(file.originalname).toLowerCase();
      allowedExts.includes(ext) ? cb(null, true) : cb(new Error('仅支持PNG/JPEG/JPG格式'), false);
    },
  }))
  async create(
    @Body() productData: Omit<Product, 'image'>,
    @UploadedFile() image: Express.Multer.File,
  ): Promise<any> {
    // 转换数据类型并拼接文件路径
    const product = {
      ...productData,
      image: image ? `/uploads/${image.filename}` : '', // 存储相对路径
      sale: productData.sale === 'true',
      price: parseFloat(productData.price),
      saleval: parseInt(productData.saleval),
    };
    return this.productsService.create(product);
  }
}
  1. 在main.ts中配置静态资源访问,让前端能加载上传的图片:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
import { NestExpressApplication } from '@nestjs/platform-express';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.enableCors(); // 允许跨域(前端后端端口不同时需要)
  // 配置静态资源目录
  app.useStaticAssets(join(__dirname, '..', 'uploads'), { prefix: '/uploads/' });
  await app.listen(3000);
}
bootstrap();
  1. 确保PostgreSQL数据库中product表的image字段为字符串类型,用于存储文件路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:27:35