I am new to both fastAPI and React Native, I have to make an app that selects image, selects category and then uploads it using upload functionality of backend. I am using Typescript, in React Native Environment for frontend and fastAPI for backend. I don't know how to pass the data to the POST request.
This is my frontend:
import React, { useState, useCallback } from 'react';
import { Button, View, Text, Image, Platform, Alert, Linking } from 'react-native';
import ImagePicker, { Image as ImagePickerImage } from 'react-native-image-crop-picker';
import axios, { AxiosResponse } from 'axios';
import ModalSelector from 'react-native-modal-selector';
import { Picker } from '@react-native-picker/picker';
// import Crypto from 'react-native-crypto';
import mime from "mime";
const AddImage: React.FC = () => {
const [selectedImage, setSelectedImage] = useState<ImagePickerImage | null>(null);
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const category: { key: string; label: string }[] = [
{ key: 'cat1', label: 'check1' },
{ key: 'cat2', label: 'check2' },
{ key: 'cat3', label: 'check3' },
];
const selectCategory = useCallback((option: { key: string; label: string }) => {
setSelectedCategory(option.key);
}, [setSelectedCategory]);
const selectImage = async () => {
try {
const image = await ImagePicker.openPicker({
width: 200,
height: 200,
cropping: true,
});
setSelectedImage(image);
} catch (error) {
console.log('Image selection cancelled or failed.', error);
}
};
const uploadImage = async () => {
if (!selectedImage) {
Alert.alert('Error', 'Please select an image first.');
return;
}
const formData = new FormData();
formData.append('files', {
filename: selectedImage.path.split('/').pop(),
type: mime.getType(selectedImage.path),
});
formData.append('category', selectedCategory);
try {
const response = await axios.post('http://192.168.1.3:8000/uploadfiles/', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
console.log('Image uploaded', response);
Alert.alert('Success', 'Image uploaded successfully!');
} catch (error) {
console.error('Error uploading image:', error);
Alert.alert('Error', 'Failed to upload image. Please try again.');
}
};
return (
<View>
<Text style={{ fontWeight: 'bold', fontSize: 32, color: 'black', textAlign: 'center', marginBottom: 50 }}>Add Image</Text>
<Button
onPress={selectImage}
title="Select Image"
color="black"
/>
{selectedImage && (
<Image source={{ uri: selectedImage.path }} style={{ width: 200, height: 200 }} />
)}
<ModalSelector
data={category}
initValueTextStyle={{ fontWeight: 'bold', color: 'black' }}
initValue="Select Category"
accessible={true}
// Directly pass selectCategory as the onChange handler
onChange={selectCategory}
selectStyle={{ borderWidth: 10 }}
cancelStyle={{ borderWidth: 10 }}
/>
<Button
onPress={uploadImage} // Use uploadImage directly without hardcoded values
title="Upload Image"
color="blue"
/>
</View>
);
};
export default AddImage;
This is the backend:
from fastapi import FastAPI, UploadFile, Depends, HTTPException,File,Form
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, validator
import os
import base64
from typing import List
app = FastAPI()
# Set up CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Update with your React app's URL
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class Item(BaseModel):
category: str
images: List[UploadFile]
@validator("images")
def validate_images(cls, value):
allowed_formats = ['jpeg', 'png', 'gif']
allowed_size_mb = 4000 # Adjust as needed
for image in value:
if image.content_type.lower() not in ['image/jpeg', 'image/png', 'image/gif']:
raise ValueError(f"Unsupported image format. Supported formats: JPEG, PNG, GIF")
if image.file:
file_size_mb = len(image.file.read()) / (1024 * 1024)
image.file.seek(0) # Reset file pointer
if file_size_mb > allowed_size_mb:
raise ValueError(f"Image size exceeds the allowed limit of {allowed_size_mb} MB")
return value
def save_uploaded_images(category: str, images: List[UploadFile]):
upload_directory = f"uploaded_images/{category}"
os.makedirs(upload_directory, exist_ok=True)
saved_image_paths = []
for image in images:
file_path = os.path.join(upload_directory, image.filename)
with open(file_path, "wb") as file:
file.write(image.file.read())
saved_image_paths.append(file_path)
return saved_image_paths
def get_images_by_category(category: str):
image_directory = f"uploaded_images/{category}"
images = []
for file_name in os.listdir(image_directory):
file_path = os.path.join(image_directory, file_name)
with open(file_path, "rb") as file:
image_data = base64.b64encode(file.read()).decode("utf-8")
images.append({"name": file_name, "data": image_data})
return images
@app.post("/uploadfiles/")
async def upload_images(files: List[UploadFile] = File(...), category: str = Form(...)):
try:
saved_image_paths = save_uploaded_images(category, files)
confirmation_messages = [f"Image '{os.path.basename(path)}' uploaded to '{category}' category." for path in saved_image_paths]
return JSONResponse(content={"confirmation_messages": confirmation_messages}, status_code=200)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/get_images/{category}")
async def get_images(category: str):
try:
images = get_images_by_category(category)
return {"category": category, "images": images}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))