利用github給國外文件下載加速


前言

作為一名程序員,經常需要下載一些編程相關的環境,而國內的網絡環境大家都知道,有的文件用瀏覽器是下載不動的,於是我有了利用github下載文件的想法。

我的demo項目地址:https://github.com/bobowire/wireboy.remote.download (為保護github這片凈土,已設置為私有)

參考資料:

  1. NodeJS使用node-fetch下載文件並顯示下載進度示例:https://www.jianshu.com/p/4b58711cb72a
  2. nodejs——發送郵件(帶附件):https://www.cnblogs.com/yourstars/p/6728931.html

覺得好玩的,大家可以點個贊~

下載加速原理

使用github的Action遠程執行文件下載(下載qt環境速度可以達到3mb/s),然后將下載的文件進行分片,每片15mb,分片后的文件以郵件附件的方式發送到國內郵箱,我們通過下載郵箱中的附件,將分片的附件合並成完整文件,從而實現不FQ、不用下載器也能下載國外文件的目的。

簡單點說

  1. github遠程下載
  2. 文件分片
  3. 通過郵箱發到國內
  4. 對附件進行合並

使用方法

  1. 新建github項目
  2. 創建js文件download.js文件,內容請查閱后文
  3. 創建workflows/RunDownload.yml文件,內容請查閱后文
  4. 修改download.js中的fileURL 變量值,此為文件url地址
  5. 在項目github->settings->Secrets中,點擊右上方“new responsitory secret”按鈕,添加"EMAILPASS","SENDEMAIL","TOEMAIL"變量(授權碼、發送郵箱、目標郵箱)
  6. 以上全部完成后,我們每次修改download.js文件的fileURL地址,github都會自動進行一次下載。原理請自行百度“github action”。

注意:

  1. 授權碼(EMAILPASS)是指“郵箱第三方登錄授權碼”,如何獲取授權碼,以QQ郵箱為例,請點擊:http://jingyan.baidu.com/article/fedf0737af2b4035ac8977ea.html

github Action文件(RunDownload.yml)

name: Github wireboy.remote.download
 
on:
    push:
        branches:
            - main
    schedule:
     - cron: '* * * * *'
jobs:
    build:
        runs-on: ubuntu-latest
 
        steps:
            - name: Checkout codes
              uses: actions/checkout@v2
            - name: Use Node.js
              uses: actions/setup-node@v1
              with:
                node-version: '12.x'
            - name: Run
              run: npm install
              
            - run: node download.js
              
              env:
                EMAILPASS: ${{ secrets.EMAILPASS }}
                SENDEMAIL: ${{ secrets.SENDEMAIL }}
                TOEMAIL: ${{ secrets.TOEMAIL }}

源代碼(download.js)

const fetch = require("node-fetch");
const fs = require("fs");
const path = require("path");
const progressStream = require('progress-stream');
const nodemailer = require('nodemailer');


//下載 的文件 地址 (https://nodejs.org/dist/v12.18.3/node-v12.18.3-x64.msi)
//vscode地址:https://az764295.vo.msecnd.net/stable/ea3859d4ba2f3e577a159bc91e3074c5d85c0523/VSCodeUserSetup-x64-1.52.1.exe
let fileURL = 'https://az764295.vo.msecnd.net/stable/ea3859d4ba2f3e577a159bc91e3074c5d85c0523/VSCodeUserSetup-x64-1.52.1.exe';

//分割后文件集合
let attachments = [];
//每個郵件附件最大數量
let perEmailAttachmentMaxCount = 1;
//每個附件最大大小
let attachmentMaxSize = 1024 * 1024 * 45;
//下載保存的文件路徑
let fileSavePath = path.join(__dirname, path.basename(fileURL));
//緩存文件路徑
let tmpFileSavePath = fileSavePath + ".tmp";
//創建寫入流
const fileStream = fs.createWriteStream(tmpFileSavePath).on('error', function (e) {
    console.error('error==>', e)
}).on('ready', function () {
    console.log("開始下載:", fileURL);
}).on('finish', function () {
    //下載完成后重命名文件
    fs.renameSync(tmpFileSavePath, fileSavePath);
    console.log('文件下載完成:', fileSavePath);
    const readstream = fs.createReadStream(fileSavePath);
	let i = 0;
	console.time('readtime');
    let patchIndex = 0;


	readstream.on('readable', () => {
        {
            let chunk = readstream.read(attachmentMaxSize);
            while (null !== chunk) {
                patchIndex = patchIndex + 1;
                // console.log('read times:'+patchIndex)
                // console.log(fileSavePath+'.email_'+patchIndex);

                let emailFilePath = fileSavePath+'.email_'+patchIndex;
                let emailFile = fs.createWriteStream(emailFilePath);
                emailFile.write(chunk);
                emailFile.end();
                
                attachments.push({
                    filename: patchIndex+'_'+path.basename(fileURL),
                    path: emailFilePath,
                });

		        chunk = readstream.read(attachmentMaxSize);
            }
        }
	});
	readstream.on('close', () => {
        console.timeEnd('readtime');
        if(attachments.length > 1)
        {
            attachments.push({
                filename: '文件分割合並器(SplitMergeFile).exe',
                path: path.join(__dirname, 'SplitMergeFile.exe')
            });
        }
        let sendIndex = 1;
        let sendFiles = [];
        let total = attachments.length / perEmailAttachmentMaxCount;
        var i = 0;
        for(i = 0; i < attachments.length; i++)
        {
            sendFiles.push(attachments[i]);
            if(sendFiles.length >= perEmailAttachmentMaxCount)
            {
                sendEmail(sendFiles,sendIndex,total);
                sendFiles = [];
                sendIndex += 1;
            }
        }
        if(sendFiles.length > 0)
        {
            sendEmail(sendFiles,sendIndex,total);
            sendFiles = [];
        }

    });
    
});

var sendEmail = function(sendFiles,patchIndex,total){
    let msg = createEmailMessage(path.basename(fileURL) + '_Part' + patchIndex + '/' + total, sendFiles);
    console.log('Send Mail Part_' + patchIndex + '/' + total + '   ' + path.basename(fileURL));
    var i;
    for(i = 0; i < msg.attachments.length; i++)
    {
        console.log(msg.attachments[i].path);
    }
    var transporter = createTransporter();
    transporter.sendMail(msg, (error, info) => {
        if (error) {
            console.log('Error occurred');
            console.log(error.message);
            return;
        }
        console.log(path.basename(fileURL) + '_Part' + patchIndex + ' sent successfully! ');
        // console.log('Server responded with "%s"', info.response);
        transporter.close();
    });
};

//請求文件
fetch(fileURL, {
    method: 'GET',
    headers: { 'Content-Type': 'application/octet-stream' },
    // timeout: 100,    
}).then(res => {
    //獲取請求頭中的文件大小數據
    let fsize = res.headers.get("content-length");
    //創建進度
    let str = progressStream({
        length: fsize,
        time: 100 /* ms */
    });
    // 下載進度 
    str.on('progress', function (progressData) {
        //不換行輸出
        let percentage = Math.round(progressData.percentage) + '%';
        console.log(percentage);
        // process.stdout.write('\033[2J'+);
        // console.log(progress);
        /*
        {
            percentage: 9.05,
            transferred: 949624,
            length: 10485760,
            remaining: 9536136,
            eta: 42,
            runtime: 3,
            delta: 295396,
            speed: 949624
        }
        */
    });
    res.body.pipe(str).pipe(fileStream);
}).catch(e => {
    //自定義異常處理
    console.log(e);
});



var createTransporter = function(){
    return nodemailer.createTransport({
        service: 'smtp.163.com',
        host: "smtp.163.com",
        secureConnection: true,
        port:465,
        auth: {
            user: process.env.SENDEMAIL,//發送者郵箱
            pass: process.env.EMAILPASS //郵箱第三方登錄授權碼
        },
        debug: true
    },{
        from: process.env.SENDEMAIL,//發送者郵箱
        headers: {
            'X-Laziness-level': 1000
        }
    });
} 

console.log('SMTP Configured');

var createEmailMessage = function(subject,sendFiles){
    var message = {
        // Comma separated lsit of recipients 收件人用逗號間隔
        to: process.env.TOEMAIL,
    
        // Subject of the message 信息主題
        subject:  subject,
    
        // plaintext body
        // text: '請查閱附件',
    
        // Html body
        html: '<p>下載文件成功!</p><p>下載地址:' + fileURL + '</p><p>感謝github提供的下載渠道,詳見博客《利用github給國外文件下載加速》 - https://www.cnblogs.com/zhuxiaoxiao/p/14280136.html</p>',
    
        // Apple Watch specific HTML body 蘋果手表指定HTML格式
        // watchHtml: '<b>Hello</b> to myself',
    
        // An array of attachments 附件
        attachments: sendFiles
        // [
            // String attachment
           //  {
           //      filename: 'notes.txt',
           //      content: 'Some notes about this e-mail',
           //      contentType: 'text/plain' // optional,would be detected from the filename 可選的,會檢測文件名
           //  },
           //  // Binary Buffer attchment
           //  {
           //      filename: 'image.png',
           //      content: Buffer.from('iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/' +
           //         '//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U' +
           //         'g9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC', 'base64'),
           //      cid: '00001'  // should be as unique as possible 盡可能唯一
           //  },
            // File Stream attachment
            // {
            //     filename: filename,
            //     path: filepath,
            //     // cid: '00002'  // should be as unique as possible 盡可能唯一
            //  }
        // ]
    
    };
    return message;
};




效果圖

收件箱

github的action運行日志

下載VisualCode運行圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM