Los archivos y carpetas OSS se empaquetan y descargan directamente

prefacio

OSS almacena muchos proyectos (los proyectos se editan y generan mediante la plataforma de código bajo TMagic y se cargan automáticamente en OSS), ahora los proyectos deben empaquetarse y descargarse en ZIP en el fondo de administración y los archivos no se generan localmente.

OSS Para descargar archivos de proyecto:

 

1. Implementación de ideas

  • Crear instancia de OSSClient
  • Obtener instancia de depósito
  • Obtenga toda la información de archivos necesaria, recorra cada secuencia de archivos, cree y escriba en un archivo ZIP
  • Descargar archivo ZIP

2. Implementación del código (Ir)

1. Creación de OSSClient

El código se muestra a continuación:

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. Obtenga la instancia Bucket de OSS

El código se muestra a continuación:

// 获取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. Obtenga información del archivo, recorra y escríbala en el archivo ZIP.

El código se muestra a continuación:

// 创建文件(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())
	}
}

En este momento, el archivo "cuisines.zip" se generará en "principal, ir". Pero en realidad, es posible que tengamos un requisito, es decir, al descargar y empaquetar, necesitamos modificar la información de un determinado archivo, ¿cómo lograrlo?

Esta implementación es realmente muy simple: solo necesitamos leer el archivo descargado, unirlo y reemplazarlo.

El código se muestra a continuación:

// 下载文件到流
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())
	}
}

Comparación antes y después:

4.Descarga de archivos ZIP

Si el archivo se genera localmente, solo necesita ser accesible a través de la ruta de nuestro proyecto para descargarlo. Sin embargo, no quiero generar el archivo ZIP localmente. Solo necesito descargarlo directamente al descargarlo. ¿Cómo debo hacerlo? ?

A continuación, ajustamos el código y utilizamos el marco Gin para iniciar el servicio.

El código se muestra a continuación:

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())
}

Ingrese " http://127.0.0.1:8888/download " en el navegador para descargar el archivo empaquetado

Resumir

El proyecto aquí tiene solo unos pocos MB. Si el archivo descargado es demasiado grande, no se recomienda descargarlo directamente, aún así se recomienda descargarlo localmente.

Supongo que te gusta

Origin blog.csdn.net/qq_34272964/article/details/130539135
Recomendado
Clasificación