图像文件从React Native(Expo)上传到Node服务器(Express)

问题描述 投票:0回答:1

如何使用 expo 将图像文件(png、jpeg 等)从 React Native 上传到使用 Express 的 Node 服务器?

我是移动 UI 开发的初学者,Expo 文档没有多大帮助。我还尝试使用 multer (如下所述:文件从 React Native ( expo ) 上传到 Node ( multer )),但它不起作用。

在这方面,任何建议、示例或方向都将受到高度赞赏。

node.js react-native express expo
1个回答
0
投票

处理 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)。

© www.soinside.com 2019 - 2024. All rights reserved.