OSS文件打包下载

news/2024/7/6 3:02:29 标签: go, zip, oss, 压缩, 下载

前言

OSS 存放了很多项目(项目是 TMagic 低代码平台编辑生成,自动上传 OSS),现在需要在管理后台将项目打包ZIP下载,并不在本地生成文件。

OSS 要下载项目文件:

 

一、思路实现

  • 创建 OSSClient 实例
  • 获取 Bucket 实例
  • 获取所有需要的文件信息,循环下载每个文件流,创建并写入 ZIP 存档中
  • 下载 ZIP 文件

二、代码实现(Go)

1. OSSClient 创建

代码如下:

const (
	EndPoint        = "OSS账号EndPoint"
	AccessKeyId     = "OSS账号AccessKeyId"
	AccessKeySecret = "OSS账号AccessKeySecret"
	BucketName      = "OSS账号BucketName"
	Prefix          = "cuisines/" // 文件前缀
)

// 创建OSSClient实例。
// yourEndpoint填写Bucket对应的Endpoint,以华东1(杭州)为例,填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
client, err := oss.New(EndPoint, AccessKeyId, AccessKeySecret)
if err != nil {
	log.Fatalf("creates the new client instance failed, err: %v", err.Error())
}

// 获取Bucket实例
bucket, err := client.Bucket(BucketName)
if err != nil {
	log.Fatalf("gets the bucket instance failed, err: %v", err.Error())
}

2.获取 OSS 的 Bucket 实例

代码如下:

// 获取Bucket实例
bucket, err := client.Bucket(BucketName)
if err != nil {
	log.Fatalf("gets the bucket instance failed, err: %v", err.Error())
}

// 列举所有文件。
// oss.Prefix(Prefix) 通过Prefix参数设置列举的文件前缀为"cuisines/"
// oss.MaxKeys(1000) 限制数量1000,默认100,最大值1000
lsRes, err := bucket.ListObjectsV2(oss.Prefix(Prefix), oss.MaxKeys(1000))
if err != nil {
	log.Fatalf("list the objects under the current bucket failed, err: %v", err.Error())
}

3.获取文件信息,遍历写入ZIP文件中

代码如下:

// 创建文件(cuisines.zip)
f, err := os.Create(fmt.Sprintf("%s.zip", strings.TrimSuffix(Prefix, "/")))
if err != nil {
	log.Fatalf("create file failed, err: %v", err.Error())
}
// 关闭文件,释放资源。
defer f.Close()

// 创建一个向 zip 文件中写入的 writer
zipWriter := zip.NewWriter(f)
// 关闭压缩文件
defer zipWriter.Close()

// 打印列举结果。默认情况下,一次返回100条记录。
for _, object := range lsRes.Objects {
	// log.Printf("%+v", object.Key)
	// cuisines/
	// cuisines/css/animate.css
	// cuisines/js/easing.js
	// cuisines/images/asia.jpg
	// cuisines/index.html

	// 将其分成目录和文件名部分。 path = dir + file,例:cuisines/css/animate.css,dir:cuisines/css/,file:animate.css
	dir, file := filepath.Split(object.Key)
	dir = strings.TrimPrefix(dir, Prefix)

	// 路径+文件为空,则跳过,否则创建无名文件
	if dir+file == "" {
		continue
	}

	// 下载文件到流
	body, err := bucket.GetObject(object.Key)
	if err != nil {
		log.Fatalf("downloads the object failed, err: %v", err.Error())
	}

	// 创建一个文件到ZIP中
	fileWriter, err := zipWriter.Create(dir + file)
	if err != nil {
		log.Fatalf("adds a file to the zip file failed, err: %v", err.Error())
	}

	// 写入文件内容
	if _, err := io.Copy(fileWriter, body); err != nil {
		log.Fatalf("file copy failed, err: %+v", err.Error())
	}
}

此时,在"main,go"下就会生成"cuisines.zip"文件。但现实中我们可能会存在一个需求,就是在下载打包的时候,需要修改某个文件中信息,那该如何实现呢?

这个实现其实也很简单,只需要我们读取读取下载文件,匹配替换即可。

代码如下:

// 下载文件到流
body, err := bucket.GetObject(object.Key)
if err != nil {
	log.Fatalf("downloads the object failed, err: %v", err.Error())
}
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
defer body.Close()

// 创建一个文件到ZIP中
fileWriter, err := zipWriter.Create(dir + file)
if err != nil {
	log.Fatalf("adds a file to the zip file failed, err: %v", err.Error())
}

// 读取文件数据
data, err := ioutil.ReadAll(body)
if err != nil {
	log.Fatalf("read file failed, err: %+v", err.Error())
}

// 替换 cuisines/index.html 文件中指定内容
if dir+file == "index.html" {
	var newData = strings.ReplaceAll(string(data), "<title>Home</title>", "<title>主页</title>")
	// 写入文件内容
	if _, err := io.Copy(fileWriter, strings.NewReader(newData)); err != nil {
		log.Fatalf("file copy failed, err: %+v", err.Error())
	}
} else {
	// 写入文件内容
	if _, err := io.Copy(fileWriter, body); err != nil {
		log.Fatalf("file copy failed, err: %+v", err.Error())
	}
}

前后对比:

4.ZIP 文件下载

如果生成文件到本地,只需要我们项目路由可访问即可下载,但我不想在本地生成 ZIP 文件,只需要下载的时候直接下载,那该如何做呢?

下面我们对代码调整,使用 Gin  框架启动服务。

代码如下:

package main

import (
	"archive/zip"
	"bytes"
	"context"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/gin-gonic/gin"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const (
	EndPoint        = "OSS账号EndPoint"
	AccessKeyId     = "OSS账号AccessKeyId"
	AccessKeySecret = "OSS账号AccessKeySecret"
	BucketName      = "OSS账号BucketName"
	Prefix          = "cuisines/" // 文件前缀
)

// download
func main() {
	router := gin.Default()
	router.GET("/download", zipDownload)
	err := http.ListenAndServe(":8888", router)
	if err != nil {
		log.Printf("%+v", err.Error())
	}
}

// 生成ZIP文件,直接下载
func zipDownload(c *gin.Context) {
	// 创建OSSClient实例。
	// yourEndpoint填写Bucket对应的Endpoint,以华东1(杭州)为例,填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。
	// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
	client, err := oss.New(EndPoint, AccessKeyId, AccessKeySecret)
	if err != nil {
		log.Fatalf("creates the new client instance failed, err: %v", err.Error())
	}

	// 获取Bucket实例
	bucket, err := client.Bucket(BucketName)
	if err != nil {
		log.Fatalf("gets the bucket instance failed, err: %v", err.Error())
	}

	// 列举所有文件。
	// oss.Prefix(Prefix) 通过Prefix参数设置列举的文件前缀为"cuisines/"
	// oss.MaxKeys(1000) 限制数量1000,默认100,最大值1000
	lsRes, err := bucket.ListObjectsV2(oss.Prefix(Prefix), oss.MaxKeys(1000))
	if err != nil {
		log.Fatalf("list the objects under the current bucket failed, err: %v", err.Error())
	}

	// 创建一个缓冲区来写入我们的存档。
	buf := new(bytes.Buffer)

	// 创建一个向 zip 文件中写入的 writer
	zipWriter := zip.NewWriter(buf)

	// 打印列举结果。默认情况下,一次返回100条记录。
	for _, object := range lsRes.Objects {
		// log.Printf("%+v", object.Key)
		// cuisines/
		// cuisines/css/animate.css
		// cuisines/js/easing.js
		// cuisines/images/asia.jpg
		// cuisines/index.html

		// 将其分成目录和文件名部分。 path = dir + file,例:cuisines/css/animate.css,dir:cuisines/css/,file:animate.css
		dir, file := filepath.Split(object.Key)
		dir = strings.TrimPrefix(dir, Prefix)

		// 路径+文件为空,则跳过,否则创建无名文件
		if dir+file == "" {
			continue
		}

		// 下载文件到流
		body, err := bucket.GetObject(object.Key)
		if err != nil {
			log.Fatalf("downloads the object failed, err: %v", err.Error())
		}
		// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
		defer body.Close()

		// 创建一个文件到ZIP中
		fileWriter, err := zipWriter.Create(dir + file)
		if err != nil {
			log.Fatalf("adds a file to the zip file failed, err: %v", err.Error())
		}

		// 读取文件数据
		data, err := ioutil.ReadAll(body)
		if err != nil {
			log.Fatalf("read file failed, err: %+v", err.Error())
		}

		// 替换 cuisines/index.html 文件中指定内容
		if dir+file == "index.html" {
			var newData = strings.ReplaceAll(string(data), "<title>Home</title>", "<title>主页</title>")
			// 写入文件内容
			if _, err := io.Copy(fileWriter, strings.NewReader(newData)); err != nil {
				log.Fatalf("file copy failed, err: %+v", err.Error())
			}
		} else {
			// 写入文件内容
			if _, err := io.Copy(fileWriter, body); err != nil {
				log.Fatalf("file copy failed, err: %+v", err.Error())
			}
		}
	}
	
	// 关闭压缩文件
	if err = zipWriter.Close(); err != nil {
		log.Fatalf("zip writer close failed, err: %+v", err.Error())
	}

	c.Writer.Header().Set("Content-Type", "application/octet-stream")
	disposition := fmt.Sprintf("attachment; filename=\"%s.zip\"", strings.TrimSuffix(Prefix, "/"))
	c.Writer.Header().Set("Content-Disposition", disposition)
	c.Writer.Write(buf.Bytes())
}

浏览器上输入"http://127.0.0.1:8888/download",即可下载打包文件

总结

这里项目只有几MB,如果下载的文件过大,不建议直接下载,还是建议下载到本地。


http://www.niftyadmin.cn/n/297635.html

相关文章

K8s-Pod概念、创建及常用命令

文章目录 一、Pod概念1、Pod是什么&#xff1f;2、Pod网络共享实现方式3、Pod存储共享方式4、创建Pod整体流程 二、使用YAML文件定义Pod资源1、Pod资源清单YAML文件书写技巧1. YAML语法格式&#xff1a;2. 配置Linux tab缩进两个空格3. 使用kubectl explain帮助命令 2、创建Pod…

处理 json 和 HttpMessageConverter--文件下载-ResponseEntity --SpringMVC 文件上传

目录 处理 json 和 HttpMessageConverter 处理 JSON-ResponseBody 说明: 下面是要完成的效果 准备工作 创建json.jsp 创建Dog.java , 作为返回的数据 创建JsonHandler.java 完成测试(页面方式)和 (Postman 方式) ​编辑 处理 JSON-RequestBody 应用案例 修改 json…

Leetcode 300. 最长递增子序列

Leetcode 300. 最长递增子序列 题目&#xff1a; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是…

35岁后的程序猿该何去何从

社会上35岁后程序猿的确存在一个职业偏差的问题&#xff0c;是一个普遍的社会用工问题&#xff0c;目前看来&#xff0c;这是所谓的法律法规难以做到的&#xff0c;别跟我说国外如何如何&#xff0c;没有可比性&#xff0c;本篇将从职业&#xff0c;人生的角度分析&#xff0c;…

三元表达式、列表推导式、字典生成式

目录 ❤ 三元表达式 ❤ 列表推导式 ❤ 字典生成式 ❤ zip()方法 python从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129328397?spm1001.2014.3001.5502 ❤ 三元表达式 条件成立时的返回值 if 条件 else 条件不成立时的返回值…

HAL库版FreeRTOS(中)

目录 FreeRTOS 任务切换PendSV 异常PendSV 中断服务函数FreeRTOS 确定下一个要运行的任务函数vTaskSwitchContext()函数taskSELECT_HIGHEST_PRIORITY_TASK() PendSV 异常何时触发FreeRTOS 时间片调度实验功能设计软件设计下载验证 FreeRTOS 内核控制函数FreeRTOS 内核控制函数预…

03-微服务部署2023系列-centos安装docker

centos安装docker教程 1、卸载历史安装过的docker 1.1、具体命令 yum remove docker \ ​ docker-client \ ​ docker-client-latest \ ​ docker-common \ ​ docker-latest \ ​ docker-latest-logrotate \ ​ docker-logrotate \ ​ docker-engine 1.2、如果是新机忽略当前…

大数据周会-本周学习内容总结012

开会时间&#xff1a;2023.05.07 16:00 线下会议 目录 01【es数据同步至mysql】 1.1【在es中插入数据后能够同步到mysql中】 1.2【修改与删除es中的数据】 02【nifi】 2.1【Nifi的单机及分布式集群部署】 2.2【nifi集群&#xff0c;getFile简单使用nifi】 2.3【nifi使用…