使用七牛雲打包下載文件繞坑指南以及demo(java和node雙語版本)

背景

最近在開發一個項目時,遇到了一個打包下載文件的需求。並且它不只僅是一個簡單粗暴的直接打包,還須要按照目錄層級打包。具體以下圖:java

你們開發這個需求的第一反應多是去找各類各樣的開源庫、打包工具,而後寫各類複雜的遍歷、建目錄等。然而在雲服務大行其道的今天,合理使用雲服務可以很輕鬆地解決咱們的問題。好比七牛雲服務提供的文件打包功能(mkzip),它的好處有如下幾點:node

  1. 功能接入簡單,api易理解,提供各類主流開發語言的SDK
  2. 對於少許文件與大量文件打包,提供不一樣地打包方式
  3. 能夠根據文件名或者文件別名自動創建目錄層級
  4. 問題反饋迅速。相比開源庫,你在官網提交一個工單,能夠很快地獲得售後工程師的答覆。

缺點:npm

  1. 只支持異步操做。若是想要獲取操做結果,須要調用其它api或者給七牛雲提供一個回調通知地址。json

  2. 在私有化環境當中,若是沒法連結外網的話,雲服務便沒法使用。固然通常的雲服務商也都提供私有化版本。api

接下來讓咱們具體看看如何使用七牛雲打包下載功能。另外本篇文章以實例代碼爲主,背後理論和原理請看七牛雲官方文檔:app

Demo

本demo實現的目標是將位於七牛雲服務上的兩個文件按照目錄層級打包成可下載的zip文件。兩個文件分別是https://file.demo.com/test1.txthttps://file.demo.com/test2.txt。另外咱們要將test1.txt放置在一級目錄/二級目錄文件夾下,而test2.txt則放置在根目錄下。一些共有的參數分別須要注意的是:

  • accessKey和secretKey:七牛雲服務的key配置
  • zipFileName:打包所生成的zip文件名
  • saveBucket:打包後的文件須要存儲到哪個資源空間
  • srcBucket:被打包的文件來源的資源空間。須要和saveBucket保持一致
  • notifyURL: 處理結果通知接收 URL,七牛將會向設置的URL發起 Content-Type: application/json 的 POST 請求
  • force: 1或者true會直接覆蓋原來一樣的任務操做結果,0或者false則不會覆蓋。
  • pipeline:多媒體隊列,若是沒有的話須要在管理後臺新建一個

依賴

Nodejs

npm i qiniu
複製代碼

Java

<dependency>
    <groupId>com.qiniu</groupId>
    <artifactId>qiniu-java-sdk</artifactId>
    <version>7.2.0</version>
</dependency>
複製代碼

少許文件壓縮

在及其少許文件打包的場景下,建議使用這種方式。相較於大量文件打包,這種方式更簡單直接,代碼量更少,但同時請求的命令字符串長度不能超過2048字節。

Nodejs

'use strict';

const qiniu = require('qiniu');

const accessKey = 'accessKey';
const secretKey = 'secretKey';

const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const config = new qiniu.conf.Config();

config.zone = qiniu.zone.Zone_z2;
const operManager = new qiniu.fop.OperationManager(mac, config);
// 處理指令集合
const saveBucket = 'demo-bucket';

const files = [
  {
    url: 'https://file.demo.com/test1.txt',
    alias: '一級目錄/二級目錄/test1.txt',
  },
  {
    url: 'https://file.demo.com/test2.txt',
  },
];

let urlAndAlias = '';
files.forEach(file => {
  urlAndAlias += `/url/${qiniu.util.urlsafeBase64Encode(file.url)}`;
  if (file.alias) {
    urlAndAlias += `/alias/${qiniu.util.urlsafeBase64Encode(file.alias)}`;
  }
});

const fops = [`mkzip/2${urlAndAlias}|saveas/${qiniu.util.urlsafeBase64Encode(`${saveBucket }:demo.zip`)}`];

const pipeline = 'pipeline';
const srcBucket = 'demo-bucket';
// srcKey不影響實際操做結果,可是根據七牛雲規範,須要傳入一個該資源空間實際存在的資源key。因此這裏可使用已經存在的test1.txt
const srcKey = 'test1.txt';
const options = {
  notifyURL: 'http://api.example.com/pfop/callback',
  force: true,
};
// 持久化數據處理返回的是任務的persistentId,能夠根據這個id查詢處理狀態
operManager.pfop(srcBucket, srcKey, fops, pipeline, options, (err, respBody, respInfo) => {
  if (err) {
    throw err;
  }
  if (respInfo.statusCode === 200) {
    console.log(respBody.persistentId);
  } else {
    console.log(respInfo.statusCode);
    console.log(respBody);
  }
});
複製代碼

Java

List<String> fileUrlList = new ArrayList<>(Arrays.asList("https://file.demo.com/test1.txt", "https://file.demo.com/test2.txt"));
List<String> aliasList =  new ArrayList<>(Arrays.asList("一級目錄/二級目錄/test1.txt", ""));

String urlAndAlias = "";
for(int i = 0 ; i < fileUrlList.size(); i++) {
    urlAndAlias += "/url/" + UrlSafeBase64.encodeToString(fileUrlList.get(i));
    if (StringUtils.isNotEmpty(aliasList.get(i))) {
        urlAndAlias += "/alias/" + UrlSafeBase64.encodeToString(aliasList.get(i));
    }
}

//待處理文件所在空間
String srcBucket = "demo-bucket";
String saveBucket = srcBucket;

// srcKey不影響實際操做結果,可是根據七牛雲規範,須要傳入一個該資源空間實際存在的資源key。因此這裏可使用已經存在的test1.txt
String srcKey = "test1.txt";

String zipFileName = "demo_java.zip";
Auth auth = Auth.create(accessKey, secretKey);
//數據處理指令,支持多個指令
String persistentOpfs = String.format("mkzip/2%s|saveas/%s", urlAndAlias,  UrlSafeBase64.encodeToString(saveBucket+ ":"+ zipFileName));

//其它參數
StringMap options = new StringMap();
//數據處理隊列名稱
options.put("pipeline", "pipeline");
//數據處理完成結果通知地址
options.put("notifyURL", "http://api.example.com/qiniu/pfop/notify");
options.put("force", 1);

//構造一個帶指定Zone對象的配置類
Configuration cfg = new Configuration(Zone.zone2());

OperationManager operationManager = new OperationManager(auth, cfg);
try {
    String persistentId = operationManager.pfop(srcBucket, srcKey, persistentOpfs, options);
    //能夠根據該 persistentId 查詢任務處理進度
    System.out.println(persistentId);
} catch (QiniuException e) {
    System.err.println(e.response.toString());
}
複製代碼

大量文件壓縮

爲了將大量文件壓縮,能夠將待壓縮文件url寫入一個索引文件,上傳至資源空間,再對該索引文件進行的mkzip操做。這一系列操做能夠經過2個請求完成,但也能夠只經過一個請求完成。咱們這裏只介紹第二種最便捷的方法。

Nodejs

'use strict';

const qiniu = require('qiniu');
const fs = require('fs');

const accessKey = 'accessKey';
const secretKey = 'secretKey';
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);


// 構造執行打包操做的上傳token
const srcBucket = 'demo-bucket';

const zipFileName = 'demo.zip';
const savedZipEntry = qiniu.util.urlsafeBase64Encode(`${srcBucket}:${zipFileName}`);

const persistentOps = `mkzip/4/|saveas/${savedZipEntry}`;
const options = {
  scope: srcBucket,
  persistentOps,
  // 數據處理隊列名稱,必填
  persistentPipeline: 'pipeline',
  // 數據處理完成結果通知地址
  persistentNotifyUrl: 'http://api.example.com/qiniu/pfop/notify',
};
const putPolicy = new qiniu.rs.PutPolicy(options);
const uploadToken = putPolicy.uploadToken(mac);

// 拼裝數據寫入索引文件
// files不能超過三千個
const files = [
  {
    url: 'https://file.demo.com/test1.txt',
    alias: '一級目錄/二級目錄/test1.txt',
  },
  {
    url: 'https://file.demo.com/test2.txt',
  },
];

let urlAndAlias = '';
files.forEach(file => {
  urlAndAlias += `/url/${qiniu.util.urlsafeBase64Encode(file.url)}`;
  if (file.alias) {
    urlAndAlias += `/alias/${qiniu.util.urlsafeBase64Encode(file.alias)}`;
  }
  urlAndAlias += '\n';
});
// 爲了演示方便,直接在當前目錄生成索引文件
const indexFilePath = 'index.txt';
fs.writeFileSync(indexFilePath, urlAndAlias, { encoding: 'utf-8' });

// 上傳索引文件,並在雲端執行打包操做
const config = new qiniu.conf.Config();
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra();
const key = indexFilePath;
formUploader.putFile(uploadToken, key, indexFilePath, putExtra, (respErr, respBody, respInfo) => {
  if (respErr) {
    throw respErr;
  }
  if (respInfo.statusCode === 200) {
    // 根據persistentId能夠查詢任務執行狀態
    console.log(respBody.persistentId);
  } else {
    console.log(respInfo.statusCode);
    console.log(respBody);
  }
});
複製代碼

Java

String accessKey = "accessKey";
String secretKey = "secretKey";
String zipFileName = "demo_java.zip";
String saveBucket = "demo-bucket";
String srcBucket = saveBucket;

Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
//數據處理指令
String zipFop = String.format("mkzip/4/|saveas/%s", UrlSafeBase64.encodeToString(saveBucket + ":" + zipFileName));
putPolicy.put("persistentOps", zipFop);
//數據處理隊列名稱,必填
putPolicy.put("persistentPipeline", "pipeline");
//數據處理完成結果通知地址
putPolicy.put("persistentNotifyUrl", "http://api.example.com/qiniu/pfop/notify");
long expireSeconds = 3600;
String upToken = auth.uploadToken(srcBucket, null, expireSeconds, putPolicy);
//構造一個帶指定Zone對象的配置類
Configuration cfg = new Configuration(Zone.zone2());
//...其餘參數參考類註釋
UploadManager uploadManager = new UploadManager(cfg);

//默認不指定key的狀況下,以文件內容的hash值做爲文件名
String srcKey = "index_java.txt";
List<String> fileUrlList = new ArrayList<>(Arrays.asList("https://file.demo.com/test1.txt", "https://file.demo.com/test2.txt"));
List<String> aliasList =  new ArrayList<>(Arrays.asList("一級目錄/二級目錄/test1.txt", ""));

Path path = null;
try {
    // 建立臨時索引文件
    path = Files.createTempFile("indexTempFile", ".txt");
    for(int i = 0 ; i < fileUrlList.size(); i++) {
        String line = "/url/" + UrlSafeBase64.encodeToString(fileUrlList.get(i));
        if (StringUtils.isNotEmpty(aliasList.get(i))) {
            line += "/alias/" + UrlSafeBase64.encodeToString(aliasList.get(i));
        }
        // 按行寫入
        Files.write(path,
                Arrays.asList(line),
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND);
    }
    // 應用退出時刪除臨時文件
    path.toFile().deleteOnExit();
} catch (IOException e) {

}
try {
    Response response = uploadManager.put(path.toString(), srcKey, upToken);
    //解析上傳成功的結果
    Map putRet = new Gson().fromJson(response.bodyString(), Map.class);
    // 根據persistentId能夠查詢任務執行狀態
    System.out.println(putRet.get("persistentId"));
} catch (QiniuException ex) {
    Response r = ex.response;
    System.err.println(r.toString());
    try {
        System.err.println(r.bodyString());
    } catch (QiniuException ex2) {
        //ignore
    }
}
複製代碼

調試

在開發過程中,能夠經過使用gethttps://api.qiniu.com/status/get/prefop?id={persistentId},查看打包任務的執行情況

以上回包字段與七牛雲post開發者提供的notifyURL的請求字段一致。

生成打包文件連接

將域名與zipFileName拼接便可。如http://file.demo.com/demo.zip

最後

七牛雲不只擁有壓縮打包功能,還有音頻、視頻轉碼等文件操做功能。在合適的場景下使用雲服務能夠達到事半功倍的效果。

相關文章
相關標籤/搜索