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端配置
- 先创建
uploads文件夹用于存储图片 - 修改路由代码:
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
相关产品推荐
相关产品推荐

