Reputation: 49351
This is the component I reproduced:
import Image from "next/image";
import { useState } from "react";
export default function Home() {
const [downloadURL, setDownloadURL] = useState("");
const download = async () => {
const result = await fetch("http://localhost:3000/test.jpg", {
method: "GET",
headers: {},
});
const blob = await result.blob();
const url = URL.createObjectURL(blob);
setDownloadURL(url);
};
const handleDownload = async (e) => {
try {
await download();
URL.revokeObjectURL(downloadURL);
} catch (error) {
console.error(error);
}
};
return (
<div className=" bg-gray-500 bg-opacity-75 transition-opacity flex flex-col justify-center items-center">
<Image src="/test.jpg" width={500} height={600} className="mb-2 " />
<button
onClick={handleDownload}
type="button"
className="flex-1 content-center text-center bg-indigo-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
<a href={downloadURL} download={"test"}>
Download Image
</a>
</button>
</div>
);
}
in the first click, I download .html
file and in next clicks I get the image downloaded. but I could not figure out what is causing the first click downloading the html
file.
Upvotes: 0
Views: 3880
Reputation: 4600
Few mistakes with that approach:
click
event is handled by <a>
before the <button>
's onClick.<a>
href which is bind to state, but the state is not updated immediatelly, it will be updated only on next render, but the click event will not wait for that (skipping the fact it is done with async operation).What I recommend: remove <a>
completely and use old classic download function which creates <a>
on the fly and executes "click" on it:
function Home() {
const download = (filename, content) => {
var element = document.createElement("a");
element.setAttribute("href", content);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
};
const handleDownload = async (e) => {
try {
const result = await fetch("assets/test.png", {
method: "GET",
headers: {}
});
const blob = await result.blob();
const url = URL.createObjectURL(blob);
download("test", url);
URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
}
};
return (
<div>
<img src="/assets/test.png" width={100} height={100} />
<button onClick={handleDownload} type="button">
Download Image
</button>
</div>
);
}
Sorry for simplifying your code a bit.
Upvotes: 3