In this tutorial we'll cover how to upload an image from a React Native app to a remote server.
This is just a high level overview. If you want details (upload progress, setting up a server, multiple images, etc.) please check out our class on Uploading Images in React Native.
With that said, let's get into it.
Tools
- react-native-image-picker. Allows us to access the library of images or the camera.
- Express + multer: Create a server that can accept image uploads. However you setup the backend the process will be extremely similar.
The Code
Explanation of code below.
// server.js
const express = require('express');
const multer = require('multer');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const storage = multer.diskStorage({
destination(req, file, callback) {
callback(null, './images');
},
filename(req, file, callback) {
callback(null, `${file.fieldname}_${Date.now()}_${file.originalname}`);
},
});
const upload = multer({ storage });
app.get('/', (req, res) => {
res.status(200).send('You can post to /api/upload.');
});
app.post('/api/upload', upload.array('photo', 3), (req, res) => {
console.log('file', req.files);
console.log('body', req.body);
res.status(200).json({
message: 'success!',
});
});
app.listen(process.env.PORT || 3000, () => {
console.log(
`server is running at http://localhost:${process.env.PORT || 3000}`
);
});
// App.js
import React from 'react';
import { View, Image, Button, Platform } from 'react-native';
import { launchImageLibrary } from 'react-native-image-picker';
const SERVER_URL = 'http://localhost:3000';
const createFormData = (photo, body = {}) => {
const data = new FormData();
data.append('photo', {
name: photo.fileName,
type: photo.type,
uri: Platform.OS === 'ios' ? photo.uri.replace('file://', '') : photo.uri,
});
Object.keys(body).forEach((key) => {
data.append(key, body[key]);
});
return data;
};
const App = () => {
const [photo, setPhoto] = React.useState(null);
const handleChoosePhoto = () => {
launchImageLibrary({ noData: true }, (response) => {
// console.log(response);
if (response) {
setPhoto(response);
}
});
};
const handleUploadPhoto = () => {
fetch(`${SERVER_URL}/api/upload`, {
method: 'POST',
body: createFormData(photo, { userId: '123' }),
})
.then((response) => response.json())
.then((response) => {
console.log('response', response);
})
.catch((error) => {
console.log('error', error);
});
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
{photo && (
<>
<Image
source={{ uri: photo.uri }}
style={{ width: 300, height: 300 }}
/>
<Button title="Upload Photo" onPress={handleUploadPhoto} />
</>
)}
<Button title="Choose Photo" onPress={handleChoosePhoto} />
</View>
);
};
export default App;
Explanation - server.js
The server sets the ground work for what we're doing so let's talk about that first. It's a standard Express server but our /api/upload
path uses the Multer middleware and accepts multipart/form-data
. If it sees a name
with photo
it's going to handle the image upload.
You can check out the multer documentation for details on how it's configured but this will just write files locally to the images/
directory.
The file(s) that were uploaded will be available at request.files
and any other data passed will be available at request.body
.
Explanation - App.js
Let's look at the handleChoosePhoto
function first. In it we open the image library and when a user selects an image we store that in state.
Next is handleUploadPhoto
. In it we set up a standard fetch request and set the method to POST
, which will call the /api/post
route we defined in the server.
What's different is how we pass data in the body
field.
If you're anything like me you're used to simply stringifying some JSON (JSON.stringify({ example: true })
) and calling it done.
To handle image uploads we need to set the encoding type to multipart/form-data
which means we need to format our data differently.
Thus the createFormData
function. This function will go ahead and take the image we selected and add it to the photo
field of the form data with the required info.
It will also take any other data you want to pass to it and add it to the form data, as you can see from the userId
.
With this the file will be available at request.files
and the userId will be available at request.body.userId
.
Important Note: Notice that ternary operator in the
uri
field? We have to do that because iOS prepends the path it returns with afile://
that I've found breaks file uploads so we remove it.
Conclusion
This was a super brief overview of a relatively complex subject. The reality is that uploading an image takes more time than a small JSON object and on mobile networks we have unique challenges to deal with such as:
- Slow networks
- No network connection
- Network connections stopping & starting during upload
These aren't exceptions - they're guarantees that we have to deal with. That's why we cover them in the Uploading Images in React Native class here on React Native School.
If you're interested in going deeper on this subject (or any of the others we cover in our classes) become a member!
Top comments (0)