如何基于MERN Stack实现无第三方包的MongoDB Atlas文件上传?
Got it, let's walk through how to implement file uploads to MongoDB Atlas using only the MERN stack tools you specified (no extra third-party packages beyond express, react, node, mongodb, and axios). I'll break this down into backend and frontend code with explanations:
Backend Implementation (Node.js + Express + MongoDB)
1. Initialize Project & Install Dependencies
First, set up your backend project and install the allowed packages:
mkdir mern-file-upload-backend cd mern-file-upload-backend npm init -y npm install express mongodb cors
2. Set Up Server & MongoDB Connection
Create a server.js file. We'll connect to MongoDB Atlas, and use a collection to store file metadata + Base64-encoded file content (great for smaller files; I can share a GridFS version for larger files if needed):
const express = require('express'); const { MongoClient, ObjectId } = require('mongodb'); const cors = require('cors'); const app = express(); app.use(cors()); // Increase JSON limit to handle larger Base64 strings (adjust as needed) app.use(express.json({ limit: '10mb' })); // Replace with your MongoDB Atlas connection string const mongoURI = 'mongodb+srv://<your-username>:<your-password>@cluster0.mongodb.net/?retryWrites=true&w=majority'; const client = new MongoClient(mongoURI); let db; let filesCollection; // Connect to MongoDB async function connectDB() { try { await client.connect(); db = client.db('fileUploadDB'); // Your database name filesCollection = db.collection('files'); // Collection to store files console.log('Connected to MongoDB Atlas'); } catch (err) { console.error('MongoDB connection error:', err); } } connectDB(); // Upload endpoint app.post('/api/upload', async (req, res) => { try { const { fileName, fileType, fileData } = req.body; if (!fileName || !fileType || !fileData) { return res.status(400).json({ message: 'Missing file details' }); } // Optional: Add file type validation here (e.g., only allow images/PDFs) const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']; if (!allowedTypes.includes(fileType)) { return res.status(400).json({ message: 'Invalid file type' }); } // Insert file into MongoDB const result = await filesCollection.insertOne({ fileName, fileType, fileData, uploadedAt: new Date() }); res.status(201).json({ message: 'File uploaded successfully', fileId: result.insertedId }); } catch (err) { console.error('Upload error:', err); res.status(500).json({ message: 'Failed to upload file' }); } }); // Fetch file endpoint app.get('/api/files/:fileId', async (req, res) => { try { const file = await filesCollection.findOne({ _id: new ObjectId(req.params.fileId) }); if (!file) { return res.status(404).json({ message: 'File not found' }); } res.json(file); } catch (err) { console.error('Fetch error:', err); res.status(500).json({ message: 'Failed to fetch file' }); } }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });
Don't forget to replace <your-username> and <your-password> with your MongoDB Atlas credentials.
Frontend Implementation (React + Axios)
1. Initialize React Project & Install Axios
Set up your React app and add axios for API calls:
npx create-react-app mern-file-upload-frontend cd mern-file-upload-frontend npm install axios
2. Create File Upload Component
Replace src/App.js with this component that handles file selection, Base64 conversion, upload, and download:
import { useState } from 'react'; import axios from 'axios'; function App() { const [selectedFile, setSelectedFile] = useState(null); const [uploadStatus, setUploadStatus] = useState(''); const [fileId, setFileId] = useState(''); // Convert file to Base64 string const convertToBase64 = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result.split(',')[1]); // Strip data URI prefix reader.onerror = (error) => reject(error); }); }; // Handle file selection const handleFileChange = (e) => { setSelectedFile(e.target.files[0]); setUploadStatus(''); setFileId(''); }; // Handle upload to backend const handleUpload = async () => { if (!selectedFile) { setUploadStatus('Please select a file first'); return; } try { setUploadStatus('Uploading...'); const fileData = await convertToBase64(selectedFile); const response = await axios.post('http://localhost:5000/api/upload', { fileName: selectedFile.name, fileType: selectedFile.type, fileData: fileData }); setUploadStatus('File uploaded successfully!'); setFileId(response.data.fileId); } catch (err) { setUploadStatus('Failed to upload file. Please try again.'); console.error('Upload error:', err); } }; // Handle downloading the uploaded file const handleDownload = async () => { if (!fileId) { setUploadStatus('No file uploaded yet'); return; } try { const response = await axios.get(`http://localhost:5000/api/files/${fileId}`); const { fileName, fileType, fileData } = response.data; // Convert Base64 back to blob and trigger download const blob = new Blob( [Uint8Array.from(atob(fileData), c => c.charCodeAt(0))], { type: fileType } ); const url = window.URL.createObjectURL(blob); const downloadLink = document.createElement('a'); downloadLink.href = url; downloadLink.download = fileName; document.body.appendChild(downloadLink); downloadLink.click(); window.URL.revokeObjectURL(url); document.body.removeChild(downloadLink); setUploadStatus('File downloaded successfully!'); } catch (err) { setUploadStatus('Failed to fetch file. Please try again.'); console.error('Download error:', err); } }; return ( <div className="App" style={{ padding: '2rem', maxWidth: '600px', margin: '0 auto' }}> <h1>MERN File Upload to Atlas</h1> <input type="file" onChange={handleFileChange} style={{ margin: '1rem 0' }} accept=".jpg,.png,.pdf" // Restrict file types (matches backend validation) /> <button onClick={handleUpload} style={{ padding: '0.5rem 1rem', marginRight: '1rem' }}> Upload File </button> {fileId && ( <button onClick={handleDownload} style={{ padding: '0.5rem 1rem' }}> Download Uploaded File </button> )} {uploadStatus && <p style={{ marginTop: '1rem', color: uploadStatus.includes('success') ? 'green' : 'red' }}>{uploadStatus}</p>} </div> ); } export default App;
Key Notes
- File Size Limits: MongoDB documents have a 16MB default limit. Since Base64 increases size by ~33%, this setup works for files up to ~12MB. For larger files, I can share a GridFS implementation using the official MongoDB driver (no third-party packages needed).
- Security: The backend includes basic file type validation – expand this to match your use case. Also, ensure your Atlas user has read/write permissions to your database.
- CORS: The backend uses the
corspackage (allowed per your request) to enable cross-origin requests from the React frontend.
内容的提问来源于stack exchange,提问作者sheepsbleat

