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

Vue+TS+Express+Multer上传图片至MongoDB遇问题求助

图片上传至MongoDB的问题修复方案

原代码核心问题:

  • 子组件仅获取了文件名,未读取文件实际内容
  • 父组件未使用FormData传递文件,而是将文件名转JSON发送,导致文件数据丢失
  • Express端Multer配置缺失,未正确处理文件上传流程
  • 前端展示时未使用正确的图片资源格式(如Base64或可访问的文件路径)

方案一:将图片以Base64格式存储到MongoDB

适合小体积图片,无需额外文件存储服务

子组件(AdminPage.vue)修改

<script setup lang="ts">
import { ref } from 'vue';

const movieTitle = ref("");
const moviePoster = ref<string | null>(null);

function fileInput(e: Event) {
    const inputElement = e.target as HTMLInputElement;
    const file = inputElement.files?.[0];
    if (!file) return;

    // 将文件转为Base64字符串
    const reader = new FileReader();
    reader.onload = () => {
        moviePoster.value = reader.result as string;
    };
    reader.readAsDataURL(file);
}

const emits = defineEmits<{ (e: "movieInfo", poster: string, title: string): void }>();

function emitImage() {
    if (!moviePoster.value || !movieTitle.value) return;
    emits("movieInfo", moviePoster.value, movieTitle.value);
}
</script>

<template>
  <div class="large-container">
    <div class="small-container">
        <form @submit.prevent="emitImage">
            <label for="file">Ladda up film poster</label>
            <input type="file" @change="fileInput" accept="image/*"/>
            <label for="movie-title-input">Film titel</label>
            <input type="text" v-model="movieTitle" name="movie-title-input" />
            <button>Upload image</button>
        </form>
    </div>
  </div>
</template>

父组件修改

<script setup lang="ts">
import AdminPageVue from '@/components/adminpage/AdminPage.vue';
import axios from 'axios';
import { ref } from 'vue';

const savedMovie = ref<any>(null);

async function saveMovieInfo(poster: string, movieTitle: string) {
    try {
        const response = await axios.post("http://localhost:3000/movie/savemovie", {
            poster,
            title: movieTitle
        });
        savedMovie.value = response.data;
    } catch (error) {
        console.log("Failed to save movie:", error);
    }
}
</script>

<template>
    <div>
      <AdminPageVue @movieInfo="saveMovieInfo"></AdminPageVue>
      <img :src="savedMovie.poster" alt="Uploaded Image" v-if="savedMovie" /> 
    </div>
</template>

Express路由修改

var express = require('express');
var router = express.Router();
const MovieModel = require("../models/movie_model");

router.post('/savemovie', async (req, res) => {
    const { title, poster } = req.body;
  
    try {
      const newMovie = await MovieModel.create({
        poster,
        title,
      });
  
      res.status(201).json({
        poster: newMovie.poster,
        title: newMovie.title,
      });
    } catch (error) {
      console.log("Error saving movie:", error);
      res.status(500).json({ error: "Failed to save movie" });
    }
});

module.exports = router;

方案二:图片存储到服务器,MongoDB存储访问路径

适合大体积图片,性能更优

子组件(AdminPage.vue)修改

<script setup lang="ts">
import { ref } from 'vue';

const movieTitle = ref("");
const moviePoster = ref<File | null>(null);

function fileInput(e: Event) {
    const inputElement = e.target as HTMLInputElement;
    const file = inputElement.files?.[0];
    if (file) {
        moviePoster.value = file;
    }
}

const emits = defineEmits<{ (e: "movieInfo", formData: FormData): void }>();

function emitImage() {
    if (!moviePoster.value || !movieTitle.value) return;
    
    // 用FormData封装文件和标题
    const formData = new FormData();
    formData.append('poster', moviePoster.value);
    formData.append('title', movieTitle.value);
    
    emits("movieInfo", formData);
}
</script>

<template>
  <div class="large-container">
    <div class="small-container">
        <form @submit.prevent="emitImage" enctype="multipart/form-data">
            <label for="file">Ladda up film poster</label>
            <input type="file" @change="fileInput" accept="image/*"/>
            <label for="movie-title-input">Film titel</label>
            <input type="text" v-model="movieTitle" name="movie-title-input" />
            <button>Upload image</button>
        </form>
    </div>
  </div>
</template>

父组件修改

<script setup lang="ts">
import AdminPageVue from '@/components/adminpage/AdminPage.vue';
import axios from 'axios';
import { ref } from 'vue';

const savedMovie = ref<any>(null);

async function saveMovieInfo(formData: FormData) {
    try {
        const response = await axios.post("http://localhost:3000/movie/savemovie", formData, {
            headers: {
                'Content-Type': 'multipart/form-data'
            }
        });
        savedMovie.value = response.data;
    } catch (error) {
        console.log("Failed to save movie:", error);
    }
}
</script>

<template>
    <div>
      <AdminPageVue @movieInfo="saveMovieInfo"></AdminPageVue>
      <img :src="`http://localhost:3000/${savedMovie.poster}`" alt="Uploaded Image" v-if="savedMovie" /> 
    </div>
</template>

Express端配置

  1. 先创建uploads文件夹用于存储图片
  2. 修改路由代码:
var express = require('express');
var router = express.Router();
const multer = require("multer");
const path = require('path');
const MovieModel = require("../models/movie_model");

// 配置Multer存储
const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './uploads/');
    },
    filename: function (req, file, cb) {
        // 生成唯一文件名,避免重复
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + '-' + file.originalname);
    }
});

const upload = multer({ storage });

// 暴露uploads文件夹为静态资源
router.use('/uploads', express.static(path.join(__dirname, '../uploads')));

router.post('/savemovie', upload.single("poster"), async (req, res) => {
    const { title } = req.body;
    // 获取上传后的文件路径
    const posterPath = `uploads/${req.file.filename}`;
  
    try {
      const newMovie = await MovieModel.create({
        poster: posterPath,
        title,
      });
  
      res.status(201).json({
        poster: newMovie.poster,
        title: newMovie.title,
      });
    } catch (error) {
      console.log("Error saving movie:", error);
      res.status(500).json({ error: "Failed to save movie" });
    }
});

module.exports = router;

Mongoose Schema(无需修改)

const { default: mongoose } = require("mongoose");

const storedMovieInfo = mongoose.Schema({
    poster: String,
    title: String,
});

module.exports = mongoose.model("storedmovieinfo", storedMovieInfo);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:43:16