为什么使用Golang并发上传文件到S3存储桶会导致被取消和上下文超时?

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

Why does golang concurrent uploading of files to S3 bucket result in canceled, context deadline exceeded

问题

我已经写了一小段用于递归遍历目录并上传目录中文件的 Golang 代码。目录中大约有93K+个项目。一段时间后,我遇到了以下错误:

上传文件时出错:/Users/randolphhill/Fat-Tree-Business/SandBox/DDD/heydoc/ios/Pods/gRPC-Core/src/core/ext/transport/chttp2/alpn/alpn.h
操作错误 S3: PutObject,https 响应错误 StatusCode: 0,RequestID: ,HostID: ,已取消,上下文截止时间已过。

以下是代码片段:

func PutFile(c context.Context, api S3PutObjectAPI, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
    return api.PutObject(c, input)
}

func PutFileS3(dir, filename, bucket, reg string) error {
    var cfg aws.Config
    st, err := fthash.Filehash(dir + filename)
    if err != nil {
        panic("configuration error, " + err.Error())
        return err
    }
    m := make(map[string]string)
    m["hashcode"] = st
    cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(reg))
    if err != nil {
        panic("configuration error, " + err.Error())
    }

    client := s3.NewFromConfig(cfg)
    tmp := "backup" + dir + filename
    uri := strings.Replace(tmp, " ", "##,##", -1)
    if checkFileOnS3(client, bucket, uri, st) {
        fmt.Println(" FILE EXIST")
        return nil
    }
    file, err2 := os.Open(dir + filename)
    defer file.Close()

    if err2 != nil {
        fmt.Println("Unable to open file " + filename)
        return err2
    }

    tmp = "backup" + dir + filename
    //uri := "backup" + dir + filename
    uri = strings.Replace(tmp, " ", "##,##", -1)
    input := &s3.PutObjectInput{
        Bucket:   &bucket,
        Key:      aws.String(uri),
        Body:     file,
        Metadata: m,
    }
    ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
    defer cancelFn()
    _, err2 = PutFile(ctx, client, input)
    if err2 != nil {
        fmt.Println("Got error uploading file:", dir+filename)
        fmt.Println(err2)
        return err2
    }

    return nil
}

希望对你有帮助!

英文:

I have written a small golang piece of code to recursive traverse a directory and upload the files in the director. There are approximately 93K+ items in the directory.
After a while I get the following error:

Got error uploading file: /Users/randolphhill/Fat-Tree-Business/SandBox/DDD/heydoc/ios/Pods/gRPC-Core/src/core/ext/transport/chttp2/alpn/alpn.h
operation error S3: PutObject, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded.

Below is the code snippet

   func PutFile(c context.Context, api S3PutObjectAPI, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
return api.PutObject(c, input)
}
func PutFileS3(dir, filename, bucket, reg string) error {
var cfg aws.Config
st, err := fthash.Filehash(dir + filename)
if err != nil {
panic("configuration error, " + err.Error())
return err
}
m := make(map[string]string)
m["hashcode"] = st
cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(reg))
if err != nil {
panic("configuration error, " + err.Error())
}
client := s3.NewFromConfig(cfg)
tmp := "backup" + dir + filename
uri := strings.Replace(tmp, " ", "##,##", -1)
if checkFileOnS3(client, bucket, uri, st) {
fmt.Println(" FILE EXIST")
return nil
}
file, err2 := os.Open(dir + filename)
defer file.Close()
if err2 != nil {
fmt.Println("Unable to open file " + filename)
return err2
}
tmp = "backup" + dir + filename
//uri := "backup" + dir + filename
uri = strings.Replace(tmp, " ", "##,##", -1)
input := &s3.PutObjectInput{
Bucket: &bucket,
Key:    aws.String(uri),
//Key:    &filename,
Body:     file,
Metadata: m,
}
ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancelFn()
_, err2 = PutFile(ctx, client, input)
if err2 != nil {
fmt.Println("Got error uploading file:", dir+filename)
fmt.Println(err2)
return err2
}
return nil
}

答案1

得分: 1

你在这里添加了一个10秒的超时:

        ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
        defer cancelFn()
        _, err2 = PutFile(ctx, client, input)
        if err2 != nil {
                fmt.Println("Got error uploading file:", dir+filename)
                fmt.Println(err2)
                return err2
        }

10秒后,调用PutFile将以上下文错误退出。如果你有需要更长时间上传的文件,你可能只需要增加超时时间。

英文:

You've added a 10 second timeout here:

        ctx, cancelFn := context.WithTimeout(context.TODO(), 10*time.Second)
defer cancelFn()
_, err2 = PutFile(ctx, client, input)
if err2 != nil {
fmt.Println("Got error uploading file:", dir+filename)
fmt.Println(err2)
return err2
}

After 10 seconds, the call to PutFile will exit with a context error. You likely just need to increase the timeout if you have files that take longer to upload.

答案2

得分: 0

package main

import (
	// "html/template"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)

var AccessKeyID string
var SecretAccessKey string
var MyRegion string
var MyBucket string
var filepath string

//GetEnvWithKey : get env value
func GetEnvWithKey(key string) string {
	return os.Getenv(key)
}

func LoadEnv() {
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatalf("Error loading .env file")
		os.Exit(1)
	}
}

func ConnectAws() *session.Session {
	AccessKeyID = GetEnvWithKey("AWS_ACCESS_KEY_ID")
	SecretAccessKey = GetEnvWithKey("AWS_SECRET_ACCESS_KEY")
	MyRegion = GetEnvWithKey("AWS_REGION")

	sess, err := session.NewSession(
		&aws.Config{
			Region: aws.String(MyRegion),
			Credentials: credentials.NewStaticCredentials(
				AccessKeyID,
				SecretAccessKey,
				"", // a token will be created when the session it's used.
			),
		})

	if err != nil {
		panic(err)
	}

	return sess
}

func SetupRouter(sess *session.Session) {
	router := gin.Default()

	router.Use(func(c *gin.Context) {
		c.Set("sess", sess)
		c.Next()
	})

	// router.Get("/upload", Form)
	router.POST("/upload", UploadImage)
	// router.GET("/image", controllers.DisplayImage)

	_ = router.Run(":4000")
}

func UploadImage(c *gin.Context) {
	sess := c.MustGet("sess").(*session.Session)
	uploader := s3manager.NewUploader(sess)

	MyBucket = GetEnvWithKey("BUCKET_NAME")

	file, header, err := c.Request.FormFile("photo")
	filename := header.Filename

	//upload to the s3 bucket
	up, err := uploader.Upload(&s3manager.UploadInput{
		Bucket: aws.String(MyBucket),
		//ACL:    aws.String("public-read"),
		Key:  aws.String(filename),
		Body: file,
	})

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error":    "Failed to upload file",
			"uploader": up,
		})
		return
	}
	filepath = "https://" + MyBucket + "." + "s3-" + MyRegion + ".amazonaws.com/" + filename
	c.JSON(http.StatusOK, gin.H{
		"filepath": filepath,
	})
}

func main() {
	LoadEnv()

	sess := ConnectAws()
	router := gin.Default()
	router.Use(func(c *gin.Context) {
		c.Set("sess", sess)
		c.Next()
	})

	router.POST("/upload", UploadImage)

	//router.LoadHTMLGlob("templates/*")
	//router.GET("/image", func(c *gin.Context) {
	//c.HTML(http.StatusOK, "index.tmpl", gin.H{
	//	"title": "Main website",
	//})
	//})

	_ = router.Run(":4000")
}
英文:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

package main
import (
// &quot;html/template&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;os&quot;
&quot;github.com/gin-gonic/gin&quot;
&quot;github.com/joho/godotenv&quot;
&quot;github.com/aws/aws-sdk-go/aws&quot;
&quot;github.com/aws/aws-sdk-go/aws/credentials&quot;
&quot;github.com/aws/aws-sdk-go/aws/session&quot;
&quot;github.com/aws/aws-sdk-go/service/s3/s3manager&quot;
)
var AccessKeyID string
var SecretAccessKey string
var MyRegion string
var MyBucket string
var filepath string
//GetEnvWithKey : get env value
func GetEnvWithKey(key string) string {
return os.Getenv(key)
}
func LoadEnv() {
err := godotenv.Load(&quot;.env&quot;)
if err != nil {
log.Fatalf(&quot;Error loading .env file&quot;)
os.Exit(1)
}
}
func ConnectAws() *session.Session {
AccessKeyID = GetEnvWithKey(&quot;AWS_ACCESS_KEY_ID&quot;)
SecretAccessKey = GetEnvWithKey(&quot;AWS_SECRET_ACCESS_KEY&quot;)
MyRegion = GetEnvWithKey(&quot;AWS_REGION&quot;)
sess, err := session.NewSession(
&amp;aws.Config{
Region: aws.String(MyRegion),
Credentials: credentials.NewStaticCredentials(
AccessKeyID,
SecretAccessKey,
&quot;&quot;, // a token will be created when the session it&#39;s used.
),
})
if err != nil {
panic(err)
}
return sess
}
func SetupRouter(sess *session.Session) {
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Set(&quot;sess&quot;, sess)
c.Next()
})
// router.Get(&quot;/upload&quot;, Form)
router.POST(&quot;/upload&quot;, UploadImage)
// router.GET(&quot;/image&quot;, controllers.DisplayImage)
_ = router.Run(&quot;:4000&quot;)
}
func UploadImage(c *gin.Context) {
sess := c.MustGet(&quot;sess&quot;).(*session.Session)
uploader := s3manager.NewUploader(sess)
MyBucket = GetEnvWithKey(&quot;BUCKET_NAME&quot;)
file, header, err := c.Request.FormFile(&quot;photo&quot;)
filename := header.Filename
//upload to the s3 bucket
up, err := uploader.Upload(&amp;s3manager.UploadInput{
Bucket: aws.String(MyBucket),
//ACL:    aws.String(&quot;public-read&quot;),
Key:  aws.String(filename),
Body: file,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
&quot;error&quot;:    &quot;Failed to upload file&quot;,
&quot;uploader&quot;: up,
})
return
}
filepath = &quot;https://&quot; + MyBucket + &quot;.&quot; + &quot;s3-&quot; + MyRegion + &quot;.amazonaws.com/&quot; + filename
c.JSON(http.StatusOK, gin.H{
&quot;filepath&quot;: filepath,
})
}
func main() {
LoadEnv()
sess := ConnectAws()
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Set(&quot;sess&quot;, sess)
c.Next()
})
router.POST(&quot;/upload&quot;, UploadImage)
//router.LoadHTMLGlob(&quot;templates/*&quot;)
//router.GET(&quot;/image&quot;, func(c *gin.Context) {
//c.HTML(http.StatusOK, &quot;index.tmpl&quot;, gin.H{
//	&quot;title&quot;: &quot;Main website&quot;,
//})
//})
_ = router.Run(&quot;:4000&quot;)
}

huangapple
  • 本文由 发表于 2022年2月6日 01:22:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/71000371.html
匿名

发表评论

匿名网友

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

确定