使用Express与Multer的NodeJS应用无法保存图片至磁盘的问题
图片上传功能调试问题
我正在尝试让图片上传的示例代码正常运行,但遇到了困难。我对示例中使用的所有技术都不熟悉,也不知道该采取哪些步骤进行调试,添加console.log日志的尝试也未成功。从日志和错误信息来看一切似乎正常,但图片并未保存到服务器目录,我无法找到原因,也不知道该从何处开始调试,比如如何在服务端添加日志检查图片数据是否存在。
服务端代码
const express = require("express"); const multer = require("multer"); const cors = require("cors"); // 为什么需要这个?我查了但还是不懂它的作用 const fs = require("fs"); const app = express(); const port = 3000; app.use(express.json()); app.use(cors()); // 它到底是干嘛的? const storage = multer.diskStorage({ destination: (request, file, cb) => { cb(null, "uploads_test/"); // 按我的理解应该保存到`./uploads_test/`,这个文件夹已经存在了 }, filename: (request, file, cb) => { cb(null, file.fieldname + "-" + Date.now() + ".jpg"); }, }); const upload = multer({ storage: storage }); app.post("/upload", upload.single("image"), async (request, response) => { try { await fs.promises.mkdir("uploads_test", { recursive: true }); // 这些日志都没起到作用 //console.log(request.body); //console.log(request.body.formData); // 这个直接报错 //console.log(request.body.formData[0]); response.status(200).json({ message: "image upload success", file: request.file, }); } catch (error) { console.error("Error handling image upload:", error.message); // 返回失败响应 response.status(500).json({ error: "Internal Server Error" }); } }); async function main() { if (!fs.existsSync("uploads_test")) { fs.mkdirSync("uploads_test"); } app.listen(port, () => { console.log( `server imageServer (example) is listening at http://localhost:${port}` ); }); } main().catch((error) => { console.error(error); });
React-Native端代码
import React, { useState, useEffect } from "react"; import { View, Text, Image, Button, StyleSheet } from "react-native"; import * as ImagePicker from "expo-image-picker"; import axios from "axios"; const ImageUploadScreenExpo = () => { const [hasGallaryPermissions, setHasGalleryPermissions] = useState(null); const [selectedImage, setSelectedImage] = useState(null); useEffect(() => { (async () => { const galleryStatus = await ImagePicker.requestCameraPermissionsAsync(); setHasGalleryPermissions(galleryStatus.status === "granted"); if (galleryStatus.status !== "granted") { alert("权限被拒绝"); } })(); }, []); const handleImageUploadAsync = async () => { if (!selectedImage) { alert("请先选择一张图片"); return; } const fileExtension = selectedImage.split(".").pop(); const fileType = `image/${fileExtension}`; const formData = new FormData(); formData.append("image", { uri: selectedImage, type: fileType, name: `image.${fileExtension}`, }); try { const response = await axios.post("http://192.168.0.11:3000/upload", { formData, headers: { "Content-Type": "multipart/form-data", }, }); // 日志里一直显示这个,但图片根本没保存 console.log("图片上传成功:", response.data); } catch (error) { console.error(error); } }; const selectImageAsync = async () => { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [1, 1], quality: 1, }); if (!result.canceled) { setSelectedImage(result.assets[0].uri); } else { alert("未选择图片"); } }; if (hasGallaryPermissions === false) { return <Text>无内部存储访问权限</Text>; } return ( <View style={styles.container}> <Text>选择一张图片上传:</Text> <Button title="选择图片" onPress={selectImageAsync} /> <Button title="上传图片" onPress={handleImageUploadAsync} /> {selectedImage && ( <View style={styles.imageContainer}> <Image source={{ uri: selectedImage }} style={{ width: 200, height: 200 }} /> </View> )} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "white", alignItems: "center", justifyContent: "center", }, }); export default ImageUploadScreenExpo;
调试补充(编辑内容)
我添加了以下代码获取到了有效日志:
console.log(request.body.formData); for (let key in request.body.formData) { console.log(key); console.log(request.body.formData[key]); }
输出结果:
{ _parts: [ [ 'image', [Object] ] ] } _parts [ [ 'image', { uri: 'file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Ftestapp-cac352d9-fc7a-41dc-9f00-ac0cc0ed4542/ImagePicker/3005453c.jpeg', type: 'image/jpeg', name: 'image.jpeg' } ] ]
输出里看不到图片数据,所以图片没保存也在意料之中,希望有人能指明后续调试方向。
Wireshark抓包结果
我用Wireshark捕获了TCP流,发现请求中没有图片数据,且表单数据被错误编码为JSON格式,HTTP头声明的Content-Type为application/json而非正确的multipart/form-data。
请求内容
POST /upload HTTP/1.1 accept: application/json, text/plain, */* Content-Type: application/json Content-Length: 320 Host: 192.168.0.232:3000 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/4.9.2 {"formData":{"_parts":[["image",{"uri":"file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fpicky-cac352d9-fc7a-41dc-9f00-ac0cc0ed4542/ImagePicker/740ec546-cf6d-40fd-982d-88722cafb53c.jpeg","type":"image/jpeg","name":"image.jpeg"}]]},"config":{"headers":{"Content-Type":"multipart/form-data"}}}
响应内容
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/json; charset=utf-8 Content-Length: 34 ETag: W/"22-or0YmP0zqfrLr/aFLTWZuX2LMys" Date: Tue, 12 Dec 2023 15:39:05 GMT Connection: keep-alive Keep-Alive: timeout=5 {"message":"image upload success"}
内容的提问来源于stack exchange,提问作者user2138149
相关产品推荐
相关产品推荐

