How to upload an image from a URL in React Next.js to Strapi

System Information
  • 4.2.3:
  • OSX 12.5:
  • Better sqlite3:
  • 16.14.2:
  • 8.5:
  • Yarn Version:

Hi guys, new here.

I’m building a React Next.js gui on top of the Strapi CMS.

I have a requirement to upload an image (jpg/png/etc) to Strapi from a URL in my Next.js app.

I can find plenty of documentation regarding how to upload an image using a file picker and then doing say setImage(e.target.files[0]) to select the image, but that doesn’t work when loading from a URL.

Do i need to read in the file from the URL and convert to a blob first?

Here’s the standard code which uploads an image to Strapi and connects it to an event (as in a ‘real world’ event). This works fine with a file picker, but not from a URL:

setImage(e.target.files[0])

const formData = new FormData();
formData.append(“files”, image);
formData.append(“ref”, “api::event.event”);
formData.append(“refId”, eventId);
formData.append(“field”, “image”);

const res = await fetch(${STRAPI_URL}/api/upload, {
method: ‘POST’,
body: formData,
});

Any guidance appreciated regarding how to load from a URL instead?

Cheers

1 Like

This is interesting, I haven’t tried it yet, but I think it will be cool to have that as a feature in the app that I am currently building. Here is something I found when researching, maybe it can help.

I think this can help with the first part of getting the image from url

I am putting all of this here, so I can use this as notes too.

But going to try to create react component that gets image from url, shows preview, and allows you to uploaded to strapi.

I think this is a cool idea.

If you get it working ( before me ) in NextJS let us know how you did.

I started to work on this basic implementation, it is not done, yet. But you can mess around with it to see if you can take it the rest of the way.

import { useState } from 'react'

export default function GetUrlImage() {
  const [url, setUrl] = useState('https://upload.wikimedia.org/wikipedia/en/6/6b/Hello_Web_Series_%28Wordmark%29_Logo.png')

  async function uploadImage(file) {

    const formData = new FormData();
      formData.append('file', file);
      formData.append('name', 'image.png');

    console.log(formData, "formData")

    const uploadResponse = await fetch("http://localhost:1338/api/upload/", { 
      method: 'POST',
      body: formData
    });

    const data = await uploadResponse.json()
    console.log(data)
  
  }

  function convertBlobToFile(blob, fileName){
    blob.lastModifiedDate = new Date();
    blob.name = fileName;
    return blob;
  }

  async function getImageFromURL(imageUrl) {
    
    const response = await fetch(imageUrl, {
      method: 'GET',
      headers: {}
    })

    const arrayBuffer = await response.arrayBuffer();

    console.log(arrayBuffer, "arrayBuffer")

    
    const blob = new Blob([arrayBuffer, { type: 'image/png' }]);

    const file = convertBlobToFile(blob, 'image.png')

    uploadImage(file)
    
    // const url = URL.createObjectURL(blob);
    

  }


  return (
    <div>
      <input value={url}  onChange={(e) => setUrl(e.target.value)}/>
      <button onClick={getImageFromURL}>Get Image</button>
    </div>
  )
}

Second Attempt

import { useState } from 'react'

export default function GetUrlImage() {
  const [url, setUrl] = useState('https://images.pexels.com/photos/8473212/pexels-photo-8473212.jpeg')
  const [file, setFile] = useState()
  
  async function uploadFile(file) {


    console.log(file, "shit")

    const formData = new FormData();
      formData.append('file', file);
      formData.append('name', 'testfile.png');
      formData.append('type', 'image/png');

    console.log(formData, "formData")

    const uploadResponse = await fetch("http://localhost:1338/api/upload/", { 
      method: 'POST',
      body: formData
    });

    const data = await uploadResponse.json()
    console.log(data)
  
  }


  async function getFileFromURL(imageUrl) {
    
    const response = await fetch(imageUrl, {
      method: 'GET',
      headers: {}
    })

    const blob = await response.blob();
    const file = new File([blob], 'testfile.png', { type: 'image/png' });
    setFile(file)
  }

  console.log(file, "file")
  return (
    <div>
      <input value={url}  onChange={(e) => setUrl(e.target.value)}/>
      <button onClick={getFileFromURL}>Get Image</button>
      { file && <button onClick={() => uploadFile(file)}>Upload Image</button> }
      { file && <img src={url} alt="" width={600} height={700}/> }

    </div>
  )
}

Now get this error

Content-Security-Policy: connect-src 'self' https:;img-src 'self' data: blob: https://dl.airtable.com;media-src 'self' data: blob:;default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline'

Will comeback to this when have more free time, hope this helps.

That’s awesome Paul_Brats thank you for taking an interest :pray:

Can’t believe we are the only people with this use case, so would be nice to figure this out. :+1:

I’ll check out your work and see what evolves.

Cheers

1 Like

Cool. Looking forwards to it. Not sure if you joined our Discord yet, that is another great place to ask questions.

1 Like

This is working for me :+1:

It downloads the image from imageURL and adds it to an existing Strapi collection item (eventId)

const myImage = await fetch(imageURL);
const myBlob = await myImage.blob();

const formData = new FormData();
formData.append('files', myBlob, imageURL);
formData.append('ref', 'api::event.event');
formData.append('refId', eventId);
formData.append('field', 'image');
console.log(formData);
const imageUploaded = await fetch(`${STRAPI_URL}/api/upload`, {
  method: 'POST',
  body: formData,
});

Early days testing, but image appeared in Strapi under the correct item.

1 Like

Thats awesome. Thanks for sharing your code. :slightly_smiling_face:

1 Like

You’re welcome, thanks for setting me on the right path. :+1:

1 Like