当按下按钮时,如何启动下载外部图像(AWS签名URL)?

huangapple go评论160阅读模式
英文:

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 &quot;next/image&quot;;
import { useState } from &quot;react&quot;;

// If signedSrc is available, render the image
const MyComponent = () =&gt; {
 const [signedSrc, signedSrcUpdate] = useState(null);

 return (
  &lt;&gt;
   &lt;button onClick={async () =&gt; {
    // fetch signed image url
    const response = await fetch(&#39;https://your-api/signedimageurlendpoint&#39;;
    signedSrcUpdate(response.data);
   }}
   {signedSrc &amp;&amp;
    &lt;Image
      src={signedSrc}
    /&gt;
   }
  &lt;/&gt;
 )
}

答案2

得分: 1

我找到了一个适合我的解决方案。我创建了一个函数,并使用纯JavaScript从readstream中进行下载(我还将文件大小缩小到仅为90MB),并在请求期间设置按钮进行加载。虽然等待时间很长,但还是可以接受的。以下是如何在JavaScript中下载readstream的链接:

https://stackoverflow.com/questions/63942715/how-to-download-a-readablestream-on-the-browser-that-has-been-returned-from-fetc

英文:

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.

https://stackoverflow.com/questions/63942715/how-to-download-a-readablestream-on-the-browser-that-has-been-returned-from-fetc

huangapple
  • 本文由 发表于 2021年10月19日 12:15:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/69625051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定