11
2019
12

golang http自定义接收上传文件,可以实现限速等功能

先看看普通的上传文件代码:

 func uploadfile(w http.ResponseWriter, r *http.Request){
   // 根据字段名获取表单文件
   formFile, header, err := r.FormFile("uploadfile")
   if err != nil {
      log.Printf("Get form file failed: %s\n", err)
      return
   }
   defer formFile.Close()
   // 创建保存文件
   destFile, err := os.Create("./upload/" + header.Filename)
   if err != nil {
      log.Printf("Create failed: %s\n", err)
      return
   }
   defer destFile.Close()
   
   // 读取表单文件,写入保存文件
   _, err = io.Copy(destFile, formFile)
   if err != nil {
      log.Printf("Write file failed: %s\n", err)
      return
   }
 }

这种方式比较简单,缺点是程序底层会先把文件接收并存入临时文件中,还有就是无法管控数据传输过程,比如上传速度限制。


关于如何自定义接收上传文件,我会贴出代码并讲解( 看注释 ),代码是从我自己项目中抠出来的,所以无法直接运行

// Upload 接收上传文件,支持限速
// path:    文件存放目录
// fname:   文件名,为空表示使用上传文件名
// maxSize: 文件最大字节数,必要参数
// speed:   每秒上传字节数,0表示不限速

func (c *ControllerBase) Upload(path, fname string, maxSize, speed int64)

 (stringstringint64error) {

    // c.Ctx.R 是 *http.Request
    // MultipartReader 返回一个MIME multipart读取器,上传文件就是从*multipart.Reader里读取的。

    // 不能调用ParseMultipartForm,因为会导致*multipart.Reader中的数据被自动读取,

    上传文件也会被读取并写入临时文件。

    mrerr := c.Ctx.R.MultipartReader()
    if err != nil {
        return """"0, err
    }

    var (
        filename string
        num      int64
    )

    // 循环读取post请求参数,上传文件就在其中
    for {
        // NextPart 返回multipart,multipart可能是上传文件,也可能是普通post参数
        perr := mr.NextPart()
        if err != nil {
            if err == io.EOF {
                break
            }
            return """"0, err
        }

        // 从multipart获取上传文件的参数名,如果为空表示该参数不是上传文件,

        continue处理下一个参数,直到遇到上传文件或结束。

        filename = p.FileName()
        if len(filename) == 0 {
            continue
        }
        if len(fname) == 0 {
            fname = filename
        }

        // 组装文件路径并创建文件,然后就可以直接写入。
        path += fname
        ferr := os.Create(path)
        if err != nil {
            return """"0, err
        }

        maxSize++
        if speed == 0 {
            // 正常复制数据,不限速
            numerr = io.CopyN(f, p, maxSize)
        } else {
            // 限制上传速度,使用了我的另一个限速工具,详见:blog.gaoge.ink/category/11.html
            numerr = io.CopyN(f, ratelimit.Reader(p, ratelimit.New(speed)), maxSize)
        }

        // 到这里,文件传输过程结束了。比起普通上传文件方式,

        不生成临时文件提高了效率,还可以手动处理数据传输过程

        ,来实现限速和限制文件大小等功能。


        f.Close()
        if err == io.EOF {
            return path, filename, num, nil
        }
        // 如果传输文件没有等到返回EOF,表示文件大小超过了maxSize
        os.Remove(path)
        return """", num, errors.New("文件太大")
    }
    return """"0nil
}


嗯嗯,就这样,然后该说点什么呢


谢谢阅读





« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。