如何使用 expo 将图像文件(png、jpeg 等)从 React Native 上传到使用 Express 的 Node 服务器?
我是移动 UI 开发的初学者,Expo 文档没有多大帮助。我还尝试使用 multer (如下所述:文件从 React Native ( expo ) 上传到 Node ( multer )),但它不起作用。
在这方面,任何建议、示例或方向都将受到高度赞赏。
处理 Expo 客户端和 Express 服务器之间的图像上传的一种潜在方法是将图像转换为 base64 并与 API 进行通信。请检查代码中的注释以了解每个步骤。
示例代码如下。
世博客户端 >index.tsx
import React, { useState } from 'react';
import { Text, View, StyleSheet, Image, Alert, TouchableOpacity, Platform } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as FileSystem from 'expo-file-system';
const UPLOAD_URL = `${process.env.UPLOAD_URL} || http://localhost:3000/upload`;
const PICK_IMAGE_LABEL = 'Pick an Image';
const UPLOAD_IMAGE_LABEL = 'Upload Image';
// Custom hook to handle image picking
const useImagePicker = () => {
const [selectedImage, setSelectedImage] = useState<string | undefined>(undefined);
const pickImageAsync = async () => {
try {
const result = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!result.granted) {
alert('Permission to access gallery is required!');
return;
}
const pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 1,
});
if (!pickerResult.canceled) {
setSelectedImage(pickerResult.assets[0].uri);
} else {
alert('You did not select any image.');
}
} catch (error) {
console.error('Error picking image:', error);
Alert.alert('Error', 'An error occurred while picking the image.');
}
};
return { selectedImage, pickImageAsync };
};
// Custom hook to handle image upload
const useImageUploader = (selectedImage: string | undefined) => {
const uploadImage = async () => {
if (!selectedImage) {
Alert.alert('No image selected', 'Please select an image first.');
return;
}
let base64Image;
try {
if (Platform.OS !== 'web') {
base64Image = await FileSystem.readAsStringAsync(selectedImage, {
encoding: FileSystem.EncodingType.Base64,
});
base64Image = `data:image/jpeg;base64,${base64Image}`;
} else {
base64Image = selectedImage;
}
const response = await fetch(UPLOAD_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image: base64Image }),
});
const result = await response.json();
if (result.success) {
Alert.alert('Success', 'Image uploaded successfully!');
} else {
console.log('Upload failed:', result.message);
Alert.alert('Upload Failed', result.message);
}
} catch (error) {
console.error('Error uploading the image:', error);
Alert.alert('Error', 'An error occurred while uploading the image.');
}
};
return { uploadImage };
};
export default function Index() {
const { selectedImage, pickImageAsync } = useImagePicker();
const { uploadImage } = useImageUploader(selectedImage);
return (
<View style={styles.container}>
<Text style={styles.title}>Demo App</Text>
<Text style={styles.subtitle}>Please upload an image</Text>
<TouchableOpacity style={styles.pickImageButton} onPress={pickImageAsync}>
<Text style={styles.pickImageText}>{PICK_IMAGE_LABEL}</Text>
</TouchableOpacity>
{selectedImage && (
<Image source={{ uri: selectedImage }} style={styles.imagePreview} />
)}
<TouchableOpacity style={styles.uploadButton} onPress={uploadImage}>
<Text style={styles.uploadButtonText}>{UPLOAD_IMAGE_LABEL}</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({...});
Express 服务器 > app.ts
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import path from "path";
import fs from "fs";
const PORT = process.env.PORT || 3000;
const app = express();
// Middleware setup
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
app.get("/", (req, res) => {
res.json({
message: "Hello from Express Backend!",
success: true
});
});
app.post('/', (req, res) => {
console.log(req.body)
res.status(200)
})
// Image upload route to handle Base64 encoded images
app.post('/upload', (req: any, res: any): void => {
console.log(req.body)
const { image } = req.body;
// Check if the 'image' field exists
if (!image) {
return res.status(400).json({ success: false, message: "No image provided" });
}
// Extract the Base64 string (data:image/jpeg;base64,...)
const matches = image.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/);
if (!matches || matches.length !== 3) {
return res.status(400).json({ success: false, message: "Invalid image format" });
}
const fileType = matches[1]; // Image type (jpeg, png, etc.)
const base64Data = matches[2]; // Base64 data
const buffer = Buffer.from(base64Data, 'base64'); // Convert Base64 to binary
// Define a unique filename (e.g., timestamp and file type)
const fileName = `${Date.now()}.${fileType}`;
const filePath = path.join(__dirname, 'uploads', fileName);
// Write the file to the 'uploads' directory
fs.writeFile(filePath, buffer, (err) => {
if (err) {
console.error("Error saving the file:", err);
return res.status(500).json({ success: false, message: "File upload failed" });
}
// Respond with success and the file path
res.json({
success: true,
message: "File uploaded successfully",
filePath: `/uploads/${fileName}`
});
});
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
export default app;
完整的源代码可在 GitHub 上找到:https://github.com/ashenwgt/react-native-expo-nodejs-express-demo
如果您想处理大尺寸的图像文件,可以考虑调整图像大小、更改分辨率或使用其他格式(如 webp)。