英文:
How can I initiate a download of an external Image (AWS Signed URL) on button press
问题
我正在尝试在按钮点击时从客户端的Nextjs上下载一个s3 Presigned URL的图像。
背景如下:
我在后端使用Golang。
前端使用Nextjs React框架。
我开始尝试通过向后端发起fetch调用来下载图像(因为我的AWS凭证只能通过后端访问)。然后后端会下载图像,并返回一个mime-type为"image/tiff"的下载流:
后端处理程序(batch只是特定于系统的):
// GetDownloadBatchFile handles GET requests to download the batch output file
func GetDownloadBatchFile(ctx *atreugo.RequestCtx) error {
// 解析批次ID
batchIntfID := ctx.UserValue("batch_id")
batchStrID, isStr := batchIntfID.(string)
if !isStr {
fmt.Println("用户错误。未提供批次ID")
}
batchIntID, err := strconv.Atoi(batchStrID)
if err != nil {
fmt.Println("用户错误。提供的批次ID无效")
}
// 获取批次
batch, err := models.GetBatchByID(batchIntID)
if err != nil {
fmt.Println("用户错误。未找到与提供的批次ID相关联的批次")
}
// 获取批次设置
batch.BatchSetting, err = models.GetBatchSettingByID(batch.BatchSettingID)
if err != nil {
fmt.Println("用户错误。未找到与提供的批次设置ID相关联的批次设置")
}
// 获取签名URL并下载
req, _ := util.AWSS3Svc.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(batch.BatchSetting.AWSBucket),
Key: aws.String(batch.OutputFileKey),
})
// 获取并下载预签名URL
URL, err := req.Presign(time.Hour * 1)
if err != nil {
fmt.Println("糟糕,无法生成签名的s3预览链接。")
}
resp, err := http.Get(URL)
if err != nil {
fmt.Println("系统错误。无法下载图像文件。")
}
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
}
这通常很好用,但问题是我的文件大小接近1 GB!下载时间太长,然后响应超时。
我在考虑只需将预签名URL发送到前端,然后从该URL下载,但我无法弄清楚如何从URL下载。
欢迎提供任何想法,以及关于如何在按钮按下时下载URL的任何信息。谢谢!
英文:
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!
答案1
得分: 1
我无法弄清楚如何从URL下载文件。
我不清楚你目前在Next.js中如何尝试实现这个功能,所以我会给出一个一般性的回答。
我建议使用Next/image组件,并在后端响应中获取到签名的URL后,将其传递给src
属性。
import Image from "next/image";
import { useState } from "react";
// 如果signedSrc可用,渲染图像
const MyComponent = () => {
const [signedSrc, signedSrcUpdate] = useState(null);
return (
<>
<button onClick={async () => {
// 获取签名的图像URL
const response = await fetch('https://your-api/signedimageurlendpoint');
signedSrcUpdate(response.data);
}}>
{signedSrc &&
<Image
src={signedSrc}
/>
}
</>
)
}
英文:
> 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}
/>
}
</>
)
}
答案2
得分: 1
我找到了一个适合我的解决方案。我创建了一个函数,并使用纯JavaScript从readstream中进行下载(我还将文件大小缩小到仅为90MB),并在请求期间设置按钮进行加载。虽然等待时间很长,但还是可以接受的。以下是如何在JavaScript中下载readstream的链接:
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论