3

What I want to achieve:

When the user clicks a button, the image should be saved to Photos.

What's the current behaviour:

On click, the user is sent to a new page that only that image exists.

<a href={imageUrl} target="_blank">button</a>

On click, the user is asked if they want to download the image. Upon accepting it, the image is saved in OneDrive instead of their Photos, which causes them not able to find it.

export const downloadFile = (url: string, filename: string) => {
    fetch(url)
    .then(response => {
        response.blob().then(blob => {
            let url = window.URL.createObjectURL(blob);
            let a = document.createElement('a');
            a.href = url;
            a.download = filename;
            a.click();
            a.remove();
        });
    });
}

Is there a better way to let the user download the image directly to their Photos instead of either of the two actions above?

UPDATE:

Tried with the mime-type and content-disposition, still sent to File instead of Photos.

enter image description here

And when I open the link on iOS, it pop up a dialog like this:

enter image description here

When I click "View", it will open a page with the image shown.

When I click "Download", it will download to my iCloud Drive directly without letting me choose to save it in my Photos.

4 Answers 4

0

If your back-end server defines image resource as Content-Disposition (rfc here https://datatracker.ietf.org/doc/html/rfc6266), you can just make browser to trigger image download with window.location.assign("[image-url]").
Note that you have to response Content-Type header correctly based on image's format(.jpg, .png or others) on your back-end server implementation.

7
  • I just tried with the correct content type with content-disposition settled, but it still downloads to my OneDrive instead of Photos. Tried with jpg, and Content-Type is 'image/jpeg'. Commented Jun 15, 2023 at 4:51
  • Try not to include slash('/') in your Content-Disposition filename. And what is it to do with OneDrive? Users can choose where their images to save with a browser provided dialog. Commented Jun 15, 2023 at 5:05
  • In the provided dialog, they can only choose whether to "view" or "download" the file. If they choose "download", the file goes to OneDrive directly without letting them choose where to save it. And if they choose "view", the page with just that image opens. Commented Jun 15, 2023 at 5:38
  • Are you using linux or OSX? This file dialog maybe platform variant. I have tested my answer on windows edge, ios safari and android webview on my old project before(I used this on my project, too), everything works fine. Commented Jun 15, 2023 at 5:49
  • And many browsers have file download settings for their users to chose, users can choose to save file automatically without ask and define default download location. I think that's NOT what you should concern about. Commented Jun 15, 2023 at 5:55
0

if you only want to download an image, maybe you can try this code

function onDownloadImage(src) {
  const img = new Image();
  img.crossOrigin = "anonymous";
  img.src = src;
  img.onload = () => {
    // create Canvas
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
    // for create tag anchor
    const a = document.createElement("a");
    a.download = `image-download`;
    a.href = canvas.toDataURL("image/png");
    a.click();
  };
}

after that you can call this function inside your component. for example:

return (
 <a href="#" onClick={() => onDownloadImage("url your image")}>Download Here ...</a>
)
2
  • Just to note, downloading is not the issue. The issue is I want to let my mobile users download the image into their Photos or Camera Roll rather than saving it in their storage like iCloud Drive. This code would download to their iCloud Drive for some reason. Commented Jun 15, 2023 at 6:01
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jun 16, 2023 at 1:02
0

You can use a library called File-saver. Read more about it here. It has a saveAs() function which you can use like this:

FileSaver.saveAs("https://httpbin.org/image", "image.jpg");

Hope this will help you.

1
  • DIdn't work, still sending it to File. Commented Jun 15, 2023 at 4:52
0

Finally, I found the way. Try this downloadImage function.

const downloadImage = async () => {
    try {
        const response = await fetch(yourImageUrl);
        const blob = await response.blob();
        const file = new File([blob], 'yourImageFileName.jpg', {
            type: blob.type,
        });

        if (navigator.canShare && navigator.canShare({ files: [file] })) {
            await navigator.share({
                files: [file],
                title: "Download Image",
                text: "Here is the image you wanted to download.",
            });
            console.log("Image shared successfully");
        } else {
            // Fallback method for devices that do not support Web Share API
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.style.display = "none";
            a.href = url;
            a.download = 'yourImageFileName.jpg';
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(url);
            document.body.removeChild(a);
            console.log("Image downloaded using fallback method");
        }
    } catch (error) {
        console.error("Download failed:", error);
    }
};

Web Share API Reference

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.