go 文件处理

10 篇文章 0 订阅

本地文件的读写

文件处理,一般指服务端对于文件的保存和读取

读写

go对文件的读写能力上,二进制>json>xml>文本txt>自定义文件
这里注意,如果是保存time.time,速度会下降,如果没有时间处理,速度接近二进制读写。

而文件的大小上,文件压缩后,不管是哪种格式,大小都是接近的。

json文件处理

http://c.biancheng.net/view/4545.html

写文件

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}

func main() {
    info := []Website{{"Golang", "http://c.biancheng.net/golang/", []string{"http://c.biancheng.net/cplus/", "http://c.biancheng.net/linux_tutorial/"}}, {"Java", "http://c.biancheng.net/java/", []string{"http://c.biancheng.net/socket/", "http://c.biancheng.net/python/"}}}

    // 创建文件
    filePtr, err := os.Create("info.json")
    if err != nil {
        fmt.Println("文件创建失败", err.Error())
        return
    }
    defer filePtr.Close()

    // 创建Json编码器
    encoder := json.NewEncoder(filePtr)

    err = encoder.Encode(info)
    if err != nil {
        fmt.Println("编码错误", err.Error())

    } else {
        fmt.Println("编码成功")
    }
}

读文件

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type Website struct {
    Name   string `xml:"name,attr"`
    Url    string
    Course []string
}

func main() {
    filePtr, err := os.Open("./info.json")
    if err != nil {
        fmt.Println("文件打开失败 [Err:%s]", err.Error())
        return
    }
    defer filePtr.Close()
    var info []Website
    // 创建json解码器
    decoder := json.NewDecoder(filePtr)
    err = decoder.Decode(&info)
    if err != nil {
        fmt.Println("解码失败", err.Error())
    } else {
        fmt.Println("解码成功")
        fmt.Println(info)
    }
}

服务端文件的上传和下载

文件上传

单个文件上传

采用的是gin框架,从gin.Context.FormFile(uploadFileKey)获取file,然后就是流的操作,这里采用了io工具类
下面示例中没有对file进行关闭
如果是采用ioutil可以将文件的内容读取

fileContent, err := ioutil.ReadAll(uploadFile)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"success": false,
			"msg":     "读取文件内容失败!" + err.Error(),
		})
	}
func FileUpload(ctx *gin.Context) {
	header, err := ctx.FormFile("file")
	if err != nil {
		logger.Error("FileUpload:文件名参数格式不正确:", err)
		util.Response(ctx, 500, "文件名参数格式不正确", err.Error())
		return
	}
	fileName := header.Filename
	fmt.Println("fileName:",fileName)

	if err := SaveUploadedFile(header); err != nil {
		logger.Error("FileUpload:保存失败:", err)
		util.Response(ctx, 500, "保存失败", err.Error())
		return
	}
	util.Response(ctx, 200, "ok", nil)
	return
}

func SaveUploadedFile(fileHeader *multipart.FileHeader) error {
	fileName := fileHeader.Filename
	//得到file
	inFle, err := fileHeader.Open()
	if err != nil {
		return err
	}
	defer inFle.Close()
	//创建 dst 文件
	out, err := os.Create("D:\\download\\"+fileName)
	if err != nil {
		return err
	}
	defer out.Close()
	// 拷贝文件
	_, err = io.Copy(out, inFle)
}


如果是表单填写,获取file的方式如下

type newForm struct {
	UploadKey *multipart.FileHeader `form:"upload-key"`
	Name      string                `form:"name"`
	Age       int                   `form:"age"`
}
	var form newForm
	if err := c.ShouldBind(&form); err != nil{
		//ignore
	}

重复操作file,会发现实现了文件的覆盖,所以需要移动读取的位移inFle.Seek(0, 0)

func SaveUploadedFile(fileHeader *multipart.FileHeader) error {
	fileName := fileHeader.Filename
	//得到file
	inFle, err := fileHeader.Open()
	if err != nil {
		return err
	}
	defer inFle.Close()

	//port, ip, err := util.GetRemoteServiceInfo("file-open-service")
	//if err != nil {
	//	return err
	//}

	//url := "http://" + ip + ":" + port + "/attachment/upload/sfOss"

	md5 := md5.New()
	io.Copy(md5, inFle)
	MD5Str := hex.EncodeToString(md5.Sum(nil))
	inFle.Seek(0, 0)

	fmt.Println("filepath.Base(fileName):", filepath.Base(fileName))
	var p = make(map[string]string)
	p["type"] = "3"
	p["md5"] = MD5Str
	var header = make(map[string]string)
	header["x-user-id"] = "11"
	header["x-account"] = "00"

	fileHttp, err := FileHttp("http://localhost:8099/test/file/upload", inFle, "file", filepath.Base(fileName), p, header)
	var result = make(map[string]interface{})
	json.Unmarshal(fileHttp, &result)
	return err
}

func FileHttp(url string, file multipart.File, fieldName, fileName string, formParams, header map[string]string) ([]byte, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(fieldName, fileName)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	for k, v := range formParams {
		if err := writer.WriteField(k, v); err != nil {
			return nil, err
		}
	}
	if err := writer.Close(); err != nil {
		return nil, err
	}
	//post请求
	req, err := http.NewRequest(http.MethodPost, url, body)
	if err != nil {
		return nil, err
	}
	//本地启动一个带头
	if header != nil && len(header) > 0 {
		for k, v := range header {
			req.Header.Set(k, v)
		}
	}

	req.Header.Add("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	fmt.Println("执行请求")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("error to request to the server:%s\n", err.Error())
		return nil, err
	}
	fmt.Println("执行请求结束")

	defer resp.Body.Close()
	respBody, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("resp.Body:", string(respBody))
	return respBody, err
}


大文件流上传

https://www.cnblogs.com/ahfuzhang/p/12629416.html

文件下载

关于gin框架文件的下载,网上有一个方案是返回文件的地址,https://www.jianshu.com/p/6950550b2937,
代码如下

//TODO Test资源文件下载
func DownloadFileService(c *gin.Context) {
  fileDir := c.Query("fileDir")
  fileName := c.Query("fileName")
  //打开文件
  _, errByOpenFile := os.Open(fileDir + "/" + fileName)
  //非空处理
  if common.IsEmpty(fileDir) || common.IsEmpty(fileName) || errByOpenFile != nil {
      /*c.JSON(http.StatusOK, gin.H{
          "success": false,
          "message": "失败",
          "error":   "资源不存在",
      })*/
      c.Redirect(http.StatusFound, "/404")
      return
  }
  c.Header("Content-Type", "application/octet-stream")
  c.Header("Content-Disposition", "attachment; filename="+fileName)
  c.Header("Content-Transfer-Encoding", "binary")
  c.File(fileDir + "/" + fileName)
  return
}

上面的方式有一个很大的弊端,就是一定要先对文件进行下载,而且客户端必须能否访问,但是有时候项目是没有文件系统的,所以参考https://www.jianshu.com/p/c9961b9d48df的http接口,下面代码从gin框架获取responseWriter 实现文件的下载。

	responseWriter := context.Writer
	fileName :="image.png"
	file, err := os.Open("image.png")
	if err==nil {
		responseWriter.Header().Add("Content-type", "application/octet-stream")
		responseWriter.Header().Add("content-disposition", "attachment; filename=\""+fileName+"\"")
		_, err := io.Copy(responseWriter, file)
		if err==nil {
			responseWriter.Flush()
		}
	}

将数据压缩然后下载

func JsonFileDownload(ctx *gin.Context) {
	responseWriter := ctx.Writer
	zipBuf:= new(bytes.Buffer)
	zipWriter := zip.NewWriter(zipBuf)
	content := "{}"
	fileName := "test.json"
	fWriter, err := zipWriter.Create(fileName)
	if err!=nil {
		logger.Error("zipWriter创建压缩文件失败:",err)
		ctx.Status(http.StatusInternalServerError)
		return
	}
	_, err = fWriter.Write([]byte(content))
	if err!=nil {
		logger.Error("fWriter创建压缩文件失败:",err)
		ctx.Status(http.StatusInternalServerError)
		return
	}
	err = zipWriter.Close()
	if err!=nil {
		logger.Error("关闭zip失败:",err)
		ctx.Status(http.StatusInternalServerError)
		return
	}

	zipName :="test.zip"
	responseWriter.Header().Add("Content-type", "application/octet-stream")
	responseWriter.Header().Add("content-disposition", "attachment; filename=\""+zipName+"\"")
	_, err = io.Copy(responseWriter, zipBuf)
	if err == nil {
		ctx.Status(http.StatusOK)
		responseWriter.Flush()
	} else {
		logger.Error("转换流异常:", err)
		ctx.Status(http.StatusInternalServerError)
		responseWriter.Flush()
	}
	return
}

文件下载遇到的问题

文件名乱码问题

java是这样解决的

            response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("utf-8"), "ISO_8859_1"));
            response.addHeader("Content-Length", "" + fileMetaDataVO.getFileSize());
            response.setContentType("application/octet-stream");
            IOUtils.copy(inputStream, outputStream);
func Utf8ToGbk(s []byte) (string, error) {
	reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder())
	d, err := ioutil.ReadAll(reader)
	if err != nil {
		return "", err
	}
	return string(d), nil
}

实现zip文件导出和实现zip文件的导入,数据文件是json文件

/**
* 下载分组的json文件
 */
func JsonFileDownload(ctx *gin.Context) {
	userId := ctx.Request.Header.Get(constant.X_USER_ID)
	companyId := ctx.Request.Header.Get(constant.X_COMPANY_ID)
	account := ctx.Request.Header.Get(constant.SESSION_ACCOUNT)
	user := param.UserInfo{Account: account, UserId: userId, CompanyId: companyId}
	//权限的判断
	if !GetAuth("", account, userId, companyId) {
		logger.Error(account, ":AddConfigByExcel:没有权限")
		util.Response(ctx, 500, "没有权限", nil)
		return
	}

	responseWriter := ctx.Writer
	zipBuf := new(bytes.Buffer)
	zipWriter := zip.NewWriter(zipBuf)
	content := "{}"
	fileName := "test.json"
	fWriter, err := zipWriter.Create(fileName)
	if err != nil {
		logger.Error(user.Account, ":zipWriter创建压缩文件失败:", err)
		ctx.Status(http.StatusInternalServerError)
		return
	}
	_, err = fWriter.Write([]byte(content))
	if err != nil {
		logger.Error(user.Account, ":fWriter创建压缩文件失败:", err)
		ctx.Status(http.StatusInternalServerError)
		return
	}
	err = zipWriter.Close()
	if err != nil {
		logger.Error(user.Account, ":关闭zip失败:", err)
		ctx.Status(http.StatusInternalServerError)
		return
	}

	zipName := "test.zip"
	responseWriter.Header().Add("Content-type", "application/octet-stream")
	responseWriter.Header().Add("content-disposition", "attachment; filename=\""+zipName+"\"")
	_, err = io.Copy(responseWriter, zipBuf)
	if err == nil {
		ctx.Status(http.StatusOK)
		responseWriter.Flush()
	} else {
		logger.Error(user.Account, ":转换流异常:", err)
		ctx.Status(http.StatusInternalServerError)
		responseWriter.Flush()
	}
	return
}

/**
* 上传分组的json文件
 */
func JsonFileUpload(ctx *gin.Context) {
	userId := ctx.Request.Header.Get(constant.X_USER_ID)
	companyId := ctx.Request.Header.Get(constant.X_COMPANY_ID)
	account := ctx.Request.Header.Get(constant.SESSION_ACCOUNT)
	user := param.UserInfo{Account: account, UserId: userId, CompanyId: companyId}
	//权限的判断
	if !GetAuth("", account, userId, companyId) {
		logger.Error(user.Account, ":AddConfigByExcel:没有权限")
		util.Response(ctx, 500, "没有权限", nil)
		return
	}

	//读取第一fileName的文件
	fileHeader, err := ctx.FormFile("file")
	if err != nil {
		logger.Error(account, ":文件格式错误"+err.Error())
		util.Response(ctx, 500, "文件格式错误"+err.Error(), nil)
		return
	}

	file, err := fileHeader.Open()
	if err != nil {
		logger.Error(account, ":文件格式错误"+err.Error())
		util.Response(ctx, 500, "文件格式错误"+err.Error(), nil)
		return
	}
	defer file.Close()

	// 打开一个zip格式文件
	//r, err := zip.OpenReader("file.zip")
	zipReader, err := zip.NewReader(file, fileHeader.Size)
	if err != nil {
		logger.Error(account, ":压缩包读取错误"+err.Error())
		util.Response(ctx, 500, "压缩包读取错误"+err.Error(), nil)
		return
	}
	//defer zipReader.Close()
	if len(zipReader.File) == 0 {
		logger.Error(account, ":没有分组文件"+err.Error())
		util.Response(ctx, 500, "没有分组文件:"+err.Error(), nil)
		return
	}
	for _, fileInfo := range zipReader.File {
		logger.Info("文件名:", fileInfo.Name)
		fileReader, err := fileInfo.Open()
		if err != nil {
			logger.Error("fileInfo err:", err)
			util.Response(ctx, 500, "fileInfo err"+err.Error(), nil)
			return
		}
		decoder := json.NewDecoder(fileReader)
		var result = make(map[string]interface{})
		err = decoder.Decode(&result)
		if err != nil {
			logger.Error("fileInfo 结构体解析错误:", err)
			util.Response(ctx, 500, "fileInfo 结构体解析错误:"+err.Error(), nil)
			return
		}
		fileReader.Close()
		logger.Info("文件内容:", util.GetJson(result))
	}
	return
}

Multipart client实现

文件流的请求

func FileHttp(url string, file multipart.File, fieldName, fileName string, formParams, header map[string]string) ([]byte, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(fieldName, fileName)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	for k, v := range formParams {
		if err := writer.WriteField(k, v); err != nil {
			return nil, err
		}
	}
	if err := writer.Close(); err != nil {
		return nil, err
	}
	//post请求
	req, err := http.NewRequest(http.MethodPost, url, body)
	if err != nil {
		return nil, err
	}
	//本地启动一个带头
	if header != nil && len(header) > 0 {
		for k, v := range header {
			req.Header.Set(k, v)
		}
	}

	req.Header.Add("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	fmt.Println("执行请求")
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("error to request to the server:%s\n", err.Error())
		return nil, err
	}
	fmt.Println("执行请求结束")

	defer resp.Body.Close()
	respBody, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("resp.Body:", string(respBody))
	return respBody, err
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值