Upload photo from react native app to strapi api

Viewed 1115

I started using strapi recently. I would like to upload a photo from my app (react native / expo), but I have errors that I don't understand.

First, I tested the photo upload on a server without strapi and I didn't experience any problem, the photo is sent.

const form = new FormData();
form.append('files', {uri: photoSource.uri, name: filename, type: 'image/jpeg'});

const requestConfig = {
  method: 'POST',
  headers: {
    "Authorization": "Bearer ey...",
    "Content-Type": "multipart/form-data;",
  },
  "body": form
};

const response = await fetch("http://myserveradresse/upload", requestConfig);

let responseJson = await response.json();
console.log(responseJson);

When I switched to Strapi, it didn't work anymore. As I saw that it was necessary to send a Buffer, I tried :

form.append('files', new Buffer(photoSource, 'base64'));

But it didn't work.

Before that, I also tried to use other methods to send the picture: FileSystem.uploadAsync, xmlhttprequest...

But nothing works. Strapi always sends me the same error:

Object {
  "data": Object {
    "errors": Array [
      Object {
        "id": "Upload.status.empty",
        "message": "Files are empty",
      },
    ],
  },
  "error": "Bad Request",
  "message": "Bad Request",
  "statusCode": 400,
}

I think I must have misunderstood something, but I can't figure out what.

4 Answers

Content-Type needs to have the boundary added as well, which will be auto-added by fetch (and hopefully the other players) - so leave Content-Type out of the headers. You also need to send files as a blog, and filename as third element in the append. The third element is how FormData knows it's actually a file, that's why strapi is upset about what it's getting from "files".

Here's how I package my profile photo on the user successfully:

    let formData = new FormData();
    let imgBlob = await (await fetch(photo.uri)).blob()
    
    formData.append(
      "files", imgBlob, filename)
    formData.append("refId",user.id)
    formData.append("source","users-permissions")
    formData.append("ref","user")
    formData.append("field","profilePhoto")
  
    const requestConfig = {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${store.getState().jwt}`
      },
      body: formData,
    };

Strapi is notoriously hard to upload images to so you may still end up with a 500 error next, but that's a move in the right direction.

I had same issue trying to send image from expo to strapi. I found a workaround, not best but it works. First in strapi dashboard dont set "Media" content type for Your image, use simple "Text". Then send Your file as base64 URL (code below). In strapi endpoint You can find Your file in "ctx.request.body" and use it for example to send to cloudinary.

frontend (expo):

    let { base64 } = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
      base64: true,
    });

    const file = `data:image/png;base64,${base64}`;
    const { data } = await axios.post(URL,{ base64: file },options);      
    

backend (strapi):

module.exports = {
  async create(ctx) {
    const { base64 } = ctx.request.body;

    return await cloudinary.uploader.upload(base64);
  },
};

I used the above-mentioned methods but none worked for me, Using expo-file-system did the job. The solution works for Android/IOS, not the web(mention in comments for solution for web upload to strapi) expo-file-system has a uploadAsync() method using which you can upload in binary or multipart form. Strapi

Steps:

install expo-file-system

import * as FileSystem from 'expo-file-system';
 
const uploadToStrapi = async (resource_uri)=>{
try {
     let uploading = await FileSystem.uploadAsync(
          `${strapi_upload_url}/api/upload`,
           ${resource_uri},
          {
          uploadType:FileSystem.FileSystemUploadType.MULTIPART,
          fieldName: "files", 
          });
          //returns server response
          return uploading
         } catch (error) {
         console.log("Error", error);
         return error
        }
 }

uploadType should be multipart fieldName should be "files"
resource_uri is the local URI of the file to be uploaded strapi_upload_url is the strapi server url

Note: You can use any document/file picker to get the file URI

I think, I finally found the solution after spending 2 days. This solution works good for React Native(non Expo) uploading to Strapi.

I used Fetch, as Axios not working in this case.

const formData = new FormData();
const uri = <Image/File URI>; // from any library, you just need file path



formData.append('files', {
   name: 'test.jpg',
   type: 'image/jpeg',
   uri: Platform.OS === 'ios' ? uri.replace('file://', '') : uri,
});



fetch(`http://localhost:1337/upload`, {
  method: 'POST',
  body: formData,
})
  .then(response => response.json())
  .then(response => {
    console.log('response', response);
  })
  .catch(error => {
    console.log('error', error);
  });

Note: I have not use Content-Type here as fetch automatically adds content headers.

Also, you need "name","type","uri" all 3 in order for it to work. I used "type" in formData.append as "image/jpeg", which you can specify depending on your file type.

Related