go 大文件的两种方式

一、表单上传

一种写法,(更多的内存分配):

func upload(ctx *gin.Context) {
	//标准库会将文件内容读入buffer
	file, err := ctx.FormFile("file")
	if err != nil {
		fmt.Println("获取数据失败")
		ctx.JSON(http.StatusOK, gin.H{
			"code":    1,
			"message": "获取数据失败",
		})
	} else {
		fmt.Println("接收的数据", file.Filename, file.Size)
		//保存上传文件
		filePath := filepath.Join("/sata", file.Filename)
		ctx.SaveUploadedFile(file, filePath)
		ctx.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": "success",
		})
	}
}

另一种写法,(更少的内存分配):

func uploadBig(ctx *gin.Context) {
	mr, err := ctx.Request.MultipartReader()
	if err != nil {
		fmt.Sprintln(err)
		fmt.Fprintln(ctx.Writer, err)
		return
	}

	for {
		part, err := mr.NextPart()
		//必须判断
		if err == io.EOF {
			break
		}

		name := part.FormName()
		if name == "" {
			continue
		}

		filename := part.FileName()
		filePath := filepath.Join("/sata", filename)
		out, err := os.Create(filePath)
		if err != nil {
			fmt.Sprintln(err)
			fmt.Fprintln(ctx.Writer, err)
			return
		}
		defer out.Close()

		_, err = io.Copy(out, part)

		ctx.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": "success",
		})
	}
}

go 客户端代码:

package main

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"path/filepath"

	"github.com/cheggaaa/pb"
)

type zero struct{}

func (z zero) Read(d []byte) (int, error) {
	return len(d), nil
}

var Zero io.Reader = &zero{}

// 分片大小设置为8M
const chunkSize = 8 * 1024 * 1024

var reader io.Reader

func init() {
	bar := pb.StartNew(0)
	bar.ShowSpeed = true
	bar.SetUnits(pb.U_BYTES)
	barReader := bar.NewProxyReader(Zero)
	reader = barReader
}

func upload1() {
	//准备一个buffer
	b := &bytes.Buffer{}

	//构建一个multipart writer
	multipartWriter := multipart.NewWriter(b)
	_, err := multipartWriter.CreateFormFile("file", filepath.Base("file.bin"))
	if err != nil {
		panic(err)
	}

	_, err = io.CopyN(b, reader, chunkSize)
	if err != nil {
		panic(err)
	}

	err = multipartWriter.Close()
	if err != nil {
		panic(err)
	}
	//构建一个multipart writer  end

	req, err := http.NewRequest("POST", "http://192.168.4.161:8080/upload", b)
	if err != nil {
		panic(err)
	}

	//必须设置请求头
	req.Header.Add("Content-Type", multipartWriter.FormDataContentType())
	//fmt.Println(multipartWriter.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
}

func upload2() {
	b := &bytes.Buffer{}

	_, err := io.CopyN(b, reader, chunkSize)
	if err != nil {
		panic(err)
	}

	req, err := http.NewRequest("POST", "http://192.168.4.161:8080/upload", b)
	if err != nil {
		panic(err)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
}

func main() {
	for {
		upload1()
		//upload2()
	}
}

表单上传的性能损耗:

二、文件直接放入body

服务端代码:不必担心什么边界问题,因为标准库已经做好了limitreader 限制,就和json传输一样。

经过测试:二进制文件、图片、视频均可以通过此方式传输。

如果需要分片传输,则将控制参数(chunk、chunksize)放入header、或放入body,

可以使用4字节+控制数据+文件数据的格式,其中4字节表示控制数据的长度。

 

func uploadx(ctx *gin.Context) {
	f, _ := os.Create("/sata/file.bin")
	defer f.Close()

	io.Copy(f, ctx.Request.Body)
	ctx.JSON(http.StatusOK, gin.H{
		"code":    0,
		"message": "success",
	})
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值