I'm trying to download an image from an s3 Presigned URL on a button click (from the client-side Nextjs).
Here's the background:
I'm using Golang on the backend. Nextjs React framework on the front end.
I started by trying to make a fetch call to the backend (since my AWS creds are stored accessible only via the back end). The backend would then download the image, and a download stream of mime-type "image/tiff":
Backend Handler (batch is just system-specific):
// GetDownloadBatchFile handles GET requests to download the batch output file
func GetDownloadBatchFile(ctx *atreugo.RequestCtx) error {
// Parse Batch ID
batchIntfID := ctx.UserValue("batch_id")
batchStrID, isStr := batchIntfID.(string)
if !isStr {
fmt.Println("User error. No batch ID provided")
}
batchIntID, err := strconv.Atoi(batchStrID)
if err != nil {
fmt.Println("User error. Invalid batch ID provided")
}
// Get Batch
batch, err := models.GetBatchByID(batchIntID)
if err != nil {
fmt.Println("User Error. No batch associated with provided batch ID")
}
// Get Batch Setting
batch.BatchSetting, err = models.GetBatchSettingByID(batch.BatchSettingID)
if err != nil {
fmt.Println("User Error. No batch setting associated with provided batch setting ID")
}
// Get signed url and download
req, _ := util.AWSS3Svc.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(batch.BatchSetting.AWSBucket),
Key: aws.String(batch.OutputFileKey),
})
// Get and Download Presigned URL
URL, err := req.Presign(time.Hour * 1)
if err != nil {
fmt.Println("Whoops, failed to generate signed s3 preview link.")
}
resp, err := http.Get(URL)
if err != nil {
fmt.Println("System Error. Unable to download image file.")
}
batchImage, err := tiff.Decode(resp.Body)
if err != nil {
fmt.Println(err)
}
ctx.Response.Header.Add("Content-Disposition", "attachment; filename=logo")
ctx.Response.Header.SetContentType("image/tiff")
err = tiff.Encode(ctx.Response.BodyWriter(), batchImage, &tiff.Options{Compression: tiff.Uncompressed}, uint32(batch.BatchSetting.DPI))
if err != nil {
return ctx.TextResponse(`ENCODE ERROR: ` err.Error())
}
return nil
}
This would normally work great, but the problem is my file is almost 1 GB! It takes too long to download and then the response just times out.
I'm thinking I should just send the presigned URL to the front end and download from that, but I can't figure out how to download from the URL.
All ideas welcome here, and also any information about how to download a url on button press. Thanks!
CodePudding user response:
I can't figure out how to download from the URL
I'm unclear on how you're trying currently trying to do this in Next.js, so I'll answer generally.
I would use the Next/image component and feed the signed URL to its src
prop once it is available in the response from your backend.
import Image from "next/image";
import { useState } from "react";
// If signedSrc is available, render the image
const MyComponent = () => {
const [signedSrc, signedSrcUpdate] = useState(null);
return (
<>
<button onClick={async () => {
// fetch signed image url
const response = await fetch('https://your-api/signedimageurlendpoint';
signedSrcUpdate(response.data);
}}
{signedSrc &&
<Image
src={signedSrc}
/>
}
</>
)
}
CodePudding user response:
I found a solution that worked for me. I created a function and used plain javascript to download from a readstream (I also got my file to be much smaller at only 90mb), and set the button to load during the request. It's a long wait, but it's manageable. Here's the link to how to download a readstream in js.
How to download a ReadableStream on the browser that has been returned from fetch