OSS文件打包下载

news2024/11/23 13:38:52

前言

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.coloradmin.cn/o/498945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

K8s基础6——应用配置管理方案、调度策略、污点和污点容忍

文章目录 一、应用配置管理方案1.1 ConfigMap1.1.1 注入变量1.1.2 挂载数据卷 1.2 Secret 二、调度策略2.1 nodeSelector定向调度2.1.1 正例2.1.2 反例 2.2 nodeAffinity亲和力调度2.2.1 In硬策略2.2.2 NotIn硬策略2.2.3 软策略 2.3 PodAffinity亲和力调度2.3.1 pod共存2.3.2 p…

【机器学习】信息量、香农熵、信息增益

这节可以搭配 【机器学习】Logistic回归&#xff08;重新整理&#xff09;信息量&#xff08;信息&#xff09;信息量公式的推理过程 香农熵信息增益 【机器学习】Logistic回归&#xff08;重新整理&#xff09; B站视频&#xff1a;“交叉熵”如何做损失函数&#xff1f;打包…

RabbitMQ、RabbitMQ发布/订阅模式

1.RabbiMQ RabbitMQ是一个消息中间件 MQ的基本结构 1.1RabitMQ安装 参考&#xff1a;Docker安装 Docker中部署RabbitMQ 2.入门案例 2.1.publisher实现 package cn.itcast.mq.helloworld;import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; im…

链表中倒数第k个结点

描述 输入一个链表&#xff0c;输出该链表中倒数第k个结点。 示例1 输入&#xff1a; 1,{1,2,3,4,5} 复制返回值&#xff1a; {5}看代码 struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {struct ListNode* fast pListHead, *slow pListHead;whi…

Redis实现分布式锁详细解读

文章目录 什么是分布式锁&#xff1f;如何用Redis实现分布式锁&#xff1f;分布式锁的改进锁过期处理集群环境下Redis宕机问题RedLock的引入RedLock的实现步骤RedLock带来的弊端 什么是分布式锁&#xff1f; 我们在学多线程的时候遇到过ReetrantLock&#xff0c;这种锁主要应用…

node install编译失败原因

关键报错信息 npm ERR! gyp verb check python checking for Python executable "python2.7" in the PATH npm ERR! gyp verb which failed Error: not found: python2.7 或者 npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! Whi…

车载以太网时间同步之EthTsync

车载以太网时间同步之EthTsync 前言 首先&#xff0c;请问大家几个小小问题&#xff0c;你清楚&#xff1a; 你知道EthTsync模块的主要作用是什么吗&#xff1f;EthTsync模块与其他AUTOSAR基础软件模块交互关系&#xff1b;Eth Tsync模块使用的时间同步协议是什么&#xff1f…

Java—JDK8新特性—函数式接口

目录 函数式接口 3.1 什么是函数式接口 3.2 functionalinterface注解 源码分析 3.3 Lambda表达式和函数式接口关系 3.4 使用函数式接口 函数式接口 3.1 什么是函数式接口 如果一个接口中只包含一个抽象方法&#xff0c;这个接口称为函数式接口 如果一个接口包含&#xff0…

mac php8 安装xdebug模块失败

安装 xdebug 模块,官网有详细介绍Xdebug: Documentation Installation 本机是mac php使用brew安装,想着可以直接使用以下方式安装,还是美滋滋的 但是安装途中发生了错误 PHP Warning: mkdir(): File exists in /usr/local/Cellar/php/8.0.10/share/php/pear/System.php on…

解决报错ERROR: No matching distribution found for torchvision==0.11.2+cu111

目录 一、猜测 二、验证 三、解决方案 四、检验 该报错是在按官网方法用指令&#xff1a; pip install torch1.9.1cu111 torchvision0.10.1cu111 torchaudio0.9.1 -f https://download.pytorch.org/whl/torch_stable.html 安装pytorch时出现的&#xff0c;以下是分析&#…

粗糙集属性约简方法与Python实现【1】

1. 方法概述 1.1 定义 粗糙集是波兰理工大学Z.pawlak教授提出用来研究不完整数据,不精确知识的表达、学习,归纳等的一套理论。它是一种新的处理模糊和不确定性问题的数学工具,已被广泛应用于知识发现、机器学习、决策支持、模式识别、专家系统及归纳推理等领域。 粗糙集理…

开源相亲小程序

此项目目前已完成前台开发。源码结构清晰&#xff0c;完美实现模块化组件化思想&#xff0c;易维护。 曾经&#xff0c;作者也为寻求自己的另一半苦恼&#xff0c;因为平时工作繁忙&#xff0c;交际圈窄小&#xff0c;而父母又各种催婚&#xff0c;无奈上了“XX网”去碰碰运气。…

webpack5搭建react框架-antd组件库使用

antd组件库使用 一、前言 前面已经完成了webpack5 react框架的配置搭建&#xff0c;我们在进行项目开发的时候大多还会使用第三方的组件库&#xff0c;而antd组件库在react项目中使用是非常非常多的&#xff0c;所以就将react框架使用最多的antd组件库引入并使用。 二、ant…

京东给了兄弟姐妹们稳稳的幸福

“今天我看了宿舍楼&#xff0c;我真的是气得想打人&#xff1b;我原来一直说的是高级单身公寓&#xff01;可实际情况呢&#xff1f;我说了多少遍了&#xff0c;要让员工、让兄弟们活的有尊严。而我们宿迁分公司的管理层是怎么做的呢&#xff0c;说难听的就是没有把员工当人去…

低代码平台的多租户SAAS系统实战解决方案—JeecgBoot

JeecgBoot免费低代码平台&#xff0c;提供一键切换多租户模式机制&#xff01;快速实现全系统的saas租户方案&#xff0c;通过租户ID进行数据隔离。 租户设计思路 1、开启全系统租户隔离 开启方法 将 org.jeecg.config.mybatis.MybatisPlusSaasConfig#OPEN_SYSTEM_TENANT_CO…

RabbitMQ --- 消息可靠性

消息队列在使用过程中&#xff0c;面临着很多实际问题需要思考&#xff1a; 一、消息可靠性 消息从发送&#xff0c;到消费者接收&#xff0c;会经理多个过程&#xff1a; 其中的每一步都可能导致消息丢失&#xff0c;常见的丢失原因包括&#xff1a; 发送时丢失&#xff1a; …

(java)继承和多态 (详解)

目录 1 继承 1.1为什么需要继承 1.2 继承概念 1.3 继承的语法 1.4 父类成员访问 1.4.1 子类中访问父类的成员变量 1.4.2 子类中访问父类的成员方法 1.5 super关键字 1.6 子类构造方法 1.7 super和this 1.7.1 this 1.7.2 super和this 1.8 再谈初始化 1.9 继承方…

软考信管高级——人力资源管理

人力资源管理内容 人力资源管理计划 内容&#xff1a; 角色与职责&#xff1a;定义项目所需的岗位、技能和能力项目组织图&#xff0c;说明项目所需的人员数量人员配备管理计划&#xff0c;说明需要每个团队的时间段以及有助于项目团队参与的其他重要信息 成功的项目团队的特…

Linux安装java jdk

1、检查系统中jdk 版本&#xff1a;java -version 2、检测 jdk 安装包&#xff1a;rpm -qa | grep java 3、卸载 openjdk rpm -e --nodeps tzdata-java-2017b-1.el7.noarch rpm -e --nodeps java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64 rpm -e --nodeps java-1.8.0-open…

深度学习细节总结

计算机视觉 目标检测&#xff0c;语义分割&#xff0c;目标分类 自然语言处理NLP 数据结构 数据结构 访问元素 线性回归 可以看成是一个单层的神经网络&#xff0c;有显式的解 优化算法 梯度下降&#xff0c;超参数&#xff1a;学习率、批量大小 分类回归 单层感知机…