OSS 文件文件夹 直接打包下载

news2024/11/15 9:32:29

前言

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/511453.html

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

相关文章

198页11万字智慧水务平台建设方案(word)

目 录 一、项目概述 1、建设背景 2、存在问题 2、运营分析 二、支持技术 1、3S技术 2、物联网技术 3、富客户端技术 4、移动互联网技术 三、建设目标 三、需求分析 1、系统用户 2、调度管理对象 3、业务需求分析 3.1 主要业务描述 3.2 业务需求…

Mal-PEG2000-Cy5.5;Cy5.5-PEG-Maleimide 花箐染料Cy5.5-聚乙二醇-马来酰亚胺

CY5.5-PEG-Mal&#xff0c;CY5.5-聚乙二醇-马来酰亚胺 中文名称&#xff1a;CY5.5-聚乙二醇-马来酰亚胺 英文名称&#xff1a;CY5.5-PEG-Mal&#xff0c; Cy5.5-PEG-Maleimide 溶剂&#xff1a;溶于水、氯仿&#xff0c;DMSO等常规性有机溶剂 性状&#xff1a;固体或粉末&a…

车载以太网 - SomeIP - TC8用例常见缩写

对于初接触SomeIP的朋友来说,看测试用例最大的烦恼我想不仅仅是来自纯英文的描述,更多的一定无休止的缩写,那这些缩写到底代表什么意思呢,我也是深受其害的人,因此就把对应的缩写、中文、英文整理出来了,希望能对大家有所帮助。 常见缩写及中英文注释 SomeIP常见缩写英文…

第一章 电路模型和电路定律(习题解析)

第一章 电路模型和电路定律&#xff08;习题解析&#xff09; 简介&#xff1a; 书后面的习题详解&#xff0c;主要包含的内容关联方向与非关联方向、功率平衡 电路基础&#xff08;第一章电路模型和电路定律&#xff09;第一章电路模型和电路定律&#xff08;补充&#xff09;…

Bentley ContextCapture Center操作入门(1)

什么是ContextCapture Center 使用照片或激光雷达点云为城市等最大规模的项目生成详细的 3D 实景网格。 ContextCapture Center具有强大的计算能力&#xff0c;可以轻松处理非常大的项目。它利用网格计算&#xff0c;通过在多台计算机上运行多个引擎并将它们关联到单个作业队列…

261页10万字政务服务一网通办平台建设方案(word)

目 录 第1章 项目建设概述 1.1 建设背景 1.2 建设意义 1.3 现状分析 1.4 建设目标 1.4.1 政务服务标准化 1.4.2 政务服务精准化 1.4.3 政务服务便捷化 1.4.4 政务服务平台化 1.4.5 政务服务协同化 1.5 建设原则 1.6 建设内容 1.7 编制依据 &#x…

前端本地存储方案-localForage

1 前言 前端有多种本地存储方案可供选择&#xff0c;以下是其中一些常见的方案&#xff1a; Cookie&#xff1a;Cookie是一种小型的文本文件&#xff0c;可以在浏览器中存储少量数据。Cookie通常用于存储会话信息或用户偏好设置等数据&#xff08;只能存储少量数据&#xff0…

动态规划-数字三角形模型

title: 数字三角形模型 date: 2023-05-10 14:26:11 categories: Algorithm动态规划 tags:动态规划数字三角形 数字三角形 Number Triangles 题目描述 观察下面的数字金字塔。 写一个程序来查找从最高点到底部任意处结束的路径&#xff0c;使路径经过数字的和最大。每一步可…

Cy5.5-聚乙二醇-羟基;PEG2000;Cy5.5-PEG-OH结构式以及相关信息介绍

中文名称Cy5.5-聚乙二醇-羟基 英文名称Cy5.5-PEG-OH 性状&#xff1a;蓝色或深蓝色固体或粘性液体&#xff0c;取决于分子量 溶剂&#xff1a;溶于水、氯仿、DMSO等常规性有机溶剂 激发/发射波长&#xff1a;655nm/678nm 分子量PEG:1k、2k、3.4k、5k其他分子量可以制定 分…

vue3 - 实现文字横向滚动 / 垂直翻滚功能组件,类似喇叭广告、公告、跑马灯的文字滚动效果(详细组件源码与注释,一键复制开箱即用!)

效果图 在 vue3.js 项目中,完成了文字横向 / 纵向自动重复滚动效果组件,支持设置滚动区域宽高、背景色、展示条数、滚动间隔、横向纵向-方向切换等等,丝滑流畅! 无任何第三方插件依赖,详细的示例及注释,轻松一键复制组件源码开箱即用! 第一步 创建文字滚动组件TextSc…

22年山东省B-2网页渗透测试

首先这道题目就是ip/1找flag的题目,所以我们直接来看环境: 这个题目直接使用抓包 修改头部即可拿到flag 我们接着看一道题目: 这道题目

输入url后,到页面展示出来

目录 1、用户在浏览器中输入url地址 2、缓存解析 3、浏览器进行DNS解析域名得到服务器ip地址 4、TCP三次握手建立客户端和服务器的连接 5、客户端发送HTTP请求获取服务器端的静态资源 6、服务器发送HTTP响应报文给客户端&#xff0c;客户端获取到页面静态资源 7、TCP四次…

探索文本生成世界:原理、技术与应用

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

4.进阶篇

目录 一、按照测试对象划分 1.界面测试&#xff08;UI测试&#xff09; 界面测试的常见错误&#xff1a; 2.可靠性测试 3.容错性测试 4.文档测试 5.兼容性测试 6.易用性 7.安装卸载测试 8.安全性测试 9.性能测试 10.内存泄漏 二、按照是否查看代码 1.黑盒测试 2.…

Dubbo源码解析一Dubbo SPI

Dubbo SPI 概述节点角色说明1. JDK SPI1.1 JDK SPI使用1.2 JDK SPI加载过程1.3 JDK SPI优缺点1.3.1 优点1.3.2 缺点 2. Dubbo中的SPI2.1 概述2.2 入门案例2.3 源码分析2.3.1 依赖注入2.3.2 动态增强2.3.2.1 装饰者模式2.3.2.2 Dubbo中的AOP 2.3.3 动态编译2.3.3.1 SPI中的自适应…

【数据结构】- 几个步骤教你认识并实现一个链表之带头(哨兵位)双向循环链表(中)

文章目录 前言&#x1f31f;一、带头双向循环链表&#x1f30f;1.1头删&#xff1a;&#x1f4ab;1.1.1代码&#xff1a;&#x1f4ab;1.1.2流程图&#xff1a; &#x1f30f;1.2尾删&#xff1a;&#x1f4ab;1.2.1代码&#xff1a;&#x1f4ab;1.2.2流程图&#xff1a; &…

5. Mysql索引优化实战二

MySQL性能调优 1. 分页查询优化1.1 根据自增且连续的主键排序的分页查询1.2 根据非主键字段排序的分页查询 2. Join关联查询优化 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 本节课内容&#xff1a; 1…

HTTP第七讲——HTTP报文

报文结构 拿 TCP 报文来举例&#xff0c;它在实际要传输的数据之前附加了一个 20 字节的头部数据&#xff0c;存储 TCP 协议必须的额外信息&#xff0c;例如发送方的端口号、接收方的端口号、包序号、标志位等等。 有了这个附加的 TCP 头&#xff0c;数据包才能够正确传输&…

HTML 基础知识

HTML基础知识 1. VSCode的安装与配置 下载地址 https://code.visualstudio.com/ 安装插件 Live Server Auto Rename Tag 自动格式化 点击 settings&#xff0c;然后输入format&#xff0c;然后勾选上 Format On Save。 2. HTML 基础标签 2.1 文件结构 快捷键&#xff1…

安卓实现左侧列表选择项点击,右侧fragment切换

安卓实现左侧列表选择项点击&#xff0c;右侧fragment切换 问题背景 安卓日常开发中&#xff0c;有时候需要开发页面中&#xff0c;显示左侧会列表选择项&#xff0c;点击不同的选项后右侧切换fragment显示&#xff0c;本文将介绍实现的一个思路。 问题分析 要实现的效果如…