Skip to content

Commit

Permalink
refactor: 修改文件上传接口
Browse files Browse the repository at this point in the history
* refactor: 改用image库进行图片解码

* refactor: 修改文件上传接口

* chore: 更新依赖
  • Loading branch information
SugarMGP authored Dec 14, 2024
1 parent 29d6d10 commit 6976894
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 137 deletions.
2 changes: 0 additions & 2 deletions app/apiException/apiException.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ var (
FileSizeExceedError = NewError(200515, log.LevelInfo, "文件大小超限")
FileNotImageError = NewError(200516, log.LevelInfo, "上传的文件不是图片")

NotInit = NewError(200404, log.LevelWarn, http.StatusText(http.StatusNotFound))
NotFound = NewError(200404, log.LevelWarn, http.StatusText(http.StatusNotFound))
Unknown = NewError(300500, log.LevelError, "系统异常,请稍后重试!")
)

// Error 方法实现了 error 接口,返回错误的消息内容
Expand Down
40 changes: 11 additions & 29 deletions app/controllers/objectController/upload.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//nolint:all
package objectController

import (
"errors"
"io"
"image"
"mime/multipart"

"4u-go/app/apiException"
Expand All @@ -14,8 +13,7 @@ import (
)

type uploadFileData struct {
UploadType string `form:"type" binding:"required"`
File *multipart.FileHeader `form:"file" binding:"required"`
File *multipart.FileHeader `form:"file" binding:"required"`
}

// UploadFile 上传文件
Expand All @@ -26,7 +24,6 @@ func UploadFile(c *gin.Context) {
return
}

uploadType := data.UploadType
fileSize := data.File.Size
file, err := data.File.Open()
if err != nil {
Expand All @@ -41,40 +38,25 @@ func UploadFile(c *gin.Context) {
}(file)

// 获取文件信息
contentType, fileExt, err := objectService.GetFileInfo(file, fileSize, uploadType)
if errors.Is(err, objectService.ErrSizeExceeded) {
apiException.AbortWithException(c, apiException.FileSizeExceedError, err)
if fileSize > objectService.ImageLimit {
apiException.AbortWithException(c, apiException.FileSizeExceedError, nil)
return
}
if errors.Is(err, objectService.ErrUnsupportedUploadType) {
apiException.AbortWithException(c, apiException.ParamError, err)

reader, size, err := objectService.ConvertToWebP(file)
if errors.Is(err, image.ErrFormat) {
apiException.AbortWithException(c, apiException.FileNotImageError, err)
return
}
if err != nil {
apiException.AbortWithException(c, apiException.ServerError, err)
return
}

var fileReader io.Reader = file
if uploadType == objectService.TypeImage {
reader, size, err := objectService.ConvertToWebP(file)
if err != nil {
if errors.Is(err, objectService.ErrNotImage) {
apiException.AbortWithException(c, apiException.FileNotImageError, err)
return
}
zap.L().Error("转换图片到 WebP 失败", zap.Error(err))
} else { // 若转换成功则替代原文件
fileReader = reader
fileSize = size
fileExt = ".webp"
contentType = "image/webp"
}
}
contentType := "image/webp"

// 上传文件
objectKey := objectService.GenerateObjectKey(uploadType, fileExt)
objectUrl, err := objectService.PutObject(objectKey, fileReader, fileSize, contentType)
objectKey := objectService.GenerateObjectKey("image", ".webp")
objectUrl, err := objectService.PutObject(objectKey, reader, size, contentType)
if err != nil {
apiException.AbortWithException(c, apiException.ServerError, err)
return
Expand Down
90 changes: 12 additions & 78 deletions app/services/objectService/objectService.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,35 @@ package objectService

import (
"bytes"
"errors"
"fmt"
"image"
_ "image/gif" // 注册解码器
_ "image/jpeg"
_ "image/png"
"io"
"mime/multipart"
"time"

"github.com/chai2010/webp"
"github.com/disintegration/imaging"
"github.com/dustin/go-humanize"
"github.com/gabriel-vasile/mimetype"
uuid "github.com/satori/go.uuid"
_ "golang.org/x/image/bmp" // 注册解码器
_ "golang.org/x/image/tiff"
_ "golang.org/x/image/webp"
)

var (
// ErrUnsupportedUploadType 不支持的上传类型
ErrUnsupportedUploadType = errors.New("unsupported upload type")

// ErrSizeExceeded 文件大小超限
ErrSizeExceeded = errors.New("file size exceeded")

// ErrNotImage 使用 image 类型上传非图片的文件
ErrNotImage = errors.New("file isn't a image")
)

const (
// TypeImage 图片
TypeImage = "image"

// TypeAttachment 附件
TypeAttachment = "attachment"
)

var uploadTypeLimits = map[string]int64{
TypeImage: humanize.MByte * 10,
TypeAttachment: humanize.MByte * 100,
}

// GetFileInfo 获取文件基本信息
func GetFileInfo(
file multipart.File,
fileSize int64,
uploadType string,
) (
contentType string,
fileExt string,
err error,
) {
// 检查文件大小
if err = checkFileSize(uploadType, fileSize); err != nil {
return "", "", err
}

// 通过文件头获取类型和扩展名
mimeType, mimeExt, err := getFileTypeAndExt(file)
if err != nil {
return "", "", err
}
return mimeType, mimeExt, nil
}
// ImageLimit 图片上传大小限制
const ImageLimit = humanize.MByte * 10

// GenerateObjectKey 通过 UUID 作为文件名并生成 ObjectKey
func GenerateObjectKey(uploadType string, fileExt string) string {
return fmt.Sprintf("%s/%d/%s%s", uploadType, time.Now().Year(), uuid.NewV1().String(), fileExt)
}

// checkFileSize 检查文件大小
func checkFileSize(uploadType string, size int64) error {
maxSize, ok := uploadTypeLimits[uploadType]
if !ok {
return ErrUnsupportedUploadType
}
if size > maxSize {
return ErrSizeExceeded
}
return nil
}

// getFileTypeAndExt 根据文件头(Magic Number)判断文件类型和扩展名
func getFileTypeAndExt(file multipart.File) (mimeType string, mimeExt string, err error) {
mime, err := mimetype.DetectReader(file)
if err != nil {
return "", "", err
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return "", "", err
}
return mime.String(), mime.Extension(), nil
}

// ConvertToWebP 将图片转换为 WebP 格式
func ConvertToWebP(file multipart.File) (io.Reader, int64, error) {
img, err := imaging.Decode(file)
func ConvertToWebP(reader io.Reader) (io.Reader, int64, error) {
img, _, err := image.Decode(reader)
if err != nil {
return nil, 0, fmt.Errorf("%w: %w", ErrNotImage, err)
return nil, 0, err
}

var buf bytes.Buffer
Expand Down
3 changes: 2 additions & 1 deletion app/utils/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"os"
"os/signal"
"syscall"
"time"

"4u-go/config/redis"
Expand All @@ -29,7 +30,7 @@ func Run(handler http.Handler, addr string) {

// 阻塞并监听结束信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

zap.L().Info("Shutdown Server...")
Expand Down
2 changes: 1 addition & 1 deletion config/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func Init(r *gin.Engine) {
user.POST("/login", userController.AuthByPassword)
user.POST("/login/session", userController.AuthBySession)

user.POST("/attachment", objectController.UploadFile)
user.POST("/upload", objectController.UploadFile)

user.POST("/repass", midwares.CheckLogin, userController.ChangePassword)
user.DELETE("/delete", midwares.CheckLogin, userController.DeleteAccount)
Expand Down
17 changes: 8 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ go 1.22.9

require (
github.com/chai2010/webp v1.1.1
github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.1
github.com/gabriel-vasile/mimetype v1.4.6
github.com/gin-contrib/cors v1.7.2
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
Expand All @@ -18,7 +16,8 @@ require (
github.com/silenceper/wechat/v2 v2.1.7
github.com/spf13/viper v1.19.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.29.0
golang.org/x/crypto v0.31.0
golang.org/x/image v0.23.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
Expand All @@ -36,13 +35,14 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.23.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/context v1.1.2 // indirect
Expand All @@ -54,7 +54,7 @@ require (
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand All @@ -78,10 +78,9 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.12.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/image v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
31 changes: 14 additions & 17 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
Expand Down Expand Up @@ -72,8 +70,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
Expand Down Expand Up @@ -131,8 +129,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
Expand Down Expand Up @@ -233,13 +231,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand All @@ -248,8 +245,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -270,14 +267,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down

0 comments on commit 6976894

Please sign in to comment.