使用Node.js Express构建Firebase Storage图片上传API时出现JSON解析错误的问题求助
Hey there, let's break down exactly why you're hitting this error and walk through how to fix it properly.
What's Causing the Error?
You've got two critical mismatches in your code right now:
- Client-side Content-Type mismatch: You're sending a raw
Fileobject as the request body but settingContent-Type: application/json. Files are binary data, not JSON, so Express'sexpress.json()middleware tries (and fails) to parse that binary data as valid JSON—hence the weird token error. - Server-side missing multipart handling: File uploads require the
multipart/form-datacontent type, but your server doesn't have any middleware configured to handle this format.
Step-by-Step Fixes
1. Update the Client Code
You need to wrap your file in a FormData object (the standard way to send files over HTTP) and let the browser handle the content type automatically:
const postImage = async (image) => { console.log(image); const formData = new FormData(); // Add the file to FormData—"uploadedImage" is the field name we'll use on the server formData.append('uploadedImage', image); const response = await fetch("http://localhost:8000/postImage", { method: "POST", body: formData // DO NOT set Content-Type here! The browser will auto-add the correct multipart/form-data header }); console.log(await response.json()); };
2. Configure the Server for File Uploads
We'll use multer—the most popular Express middleware for handling multipart/form-data—to process the file, then upload it directly to Firebase Storage.
Install Multer
First, install the package in your project:
npm install multer
Update Your Express Server File
Modify your main server file to initialize multer and pass it to your routes:
const express = require("express"); const morgan = require("morgan"); const cors = require('cors'); const multer = require('multer'); // Import multer const app = express(); // Settings app.set('port', process.env.PORT || 8000); app.set('json spaces', 4); // Middleware app.use(morgan("dev")); // Keep these for JSON/urlencoded requests—they won't interfere with file upload routes app.use(express.json({limit: "1000mb"})); app.use(express.urlencoded({extended: true})); app.use(cors({origin: "http://localhost:3000"})); // Configure multer to store files in memory (easy to pipe to Firebase) const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); // Pass the multer instance to your routes app.use(require("./routes/index")(upload)); app.listen(app.get('port'), () => { console.log("Server using port " + app.get('port')); });
Update Your Route to Handle Firebase Upload
First, make sure you've set up the Firebase Admin SDK (download your service account key from the Firebase Console and place it in your project). Then modify your route:
const { Router } = require('express'); const admin = require('firebase-admin'); const router = Router(); // Initialize Firebase Admin (do this once—you could also move this to your main server file) const serviceAccount = require('../path/to/your/serviceAccountKey.json'); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), storageBucket: 'your-bucket-name.appspot.com' // Replace with your Firebase Storage bucket name }); const bucket = admin.storage().bucket(); // Export the router with access to the multer upload instance module.exports = (upload) => { // Use upload.single('uploadedImage') to handle a single file with the field name we set on the client router.post('/postImage', upload.single('uploadedImage'), async (req, res) => { try { console.log("Received file:", req.file); // Now this code will run! // Check if a file was actually uploaded if (!req.file) { return res.status(400).json({ message: "No file provided" }); } // Set up the Firebase Storage file path/name const fileName = `uploads/${Date.now()}_${req.file.originalname}`; const file = bucket.file(fileName); // Create a write stream to upload the file buffer to Firebase const stream = file.createWriteStream({ metadata: { contentType: req.file.mimetype // Preserve the original file type } }); // Handle stream errors stream.on('error', (err) => { console.error(err); return res.status(500).json({ error: "Failed to upload file to Firebase" }); }); // When upload finishes, return the public URL (if needed) stream.on('finish', async () => { // Make the file public (optional—skip if you want private files) await file.makePublic(); const publicUrl = `https://storage.googleapis.com/${bucket.name}/${file.name}`; return res.status(200).json({ message: "File uploaded successfully!", fileUrl: publicUrl }); }); // Write the file buffer to the stream stream.end(req.file.buffer); } catch (error) { console.log(error); return res.status(500).json({ error: error.message }); } }); return router; };
Key Notes to Remember
- Never manually set Content-Type for FormData: The browser automatically adds the correct
multipart/form-dataheader with a boundary value—setting it manually will break parsing. - Multer storage options: We used
memoryStorage()for simplicity, which works well for small files. For large files, usediskStorage()to temporarily save files to your server, then upload to Firebase (just remember to clean up temp files afterward). - Firebase permissions: Ensure your service account has the correct Storage permissions (check the Firebase Console's IAM section if uploads fail due to permissions).
内容的提问来源于stack exchange,提问作者Jorge Sánchez

