由于下载 github 中 release 包的时候,或下载其他 jar、rust、docker 依赖的时候,速度往往只有 100KB 不到,所以,探讨一种下载海外资源的加速方式。

基本思路

使用海外的函数计算服务,将文件快速下载到指定的存储系统,这里典型的搭配是:

  • AWS 方案:Lambda + S3
    • 此方案有个好处是 Lambda 可以返回 HTML 页面,阿里云 FC 由于备案审核等问题,无法显示页面,会被强制下载。
  • Aliyun 方案:FC + OSS

AWS 方案

AWS 方案在开源社区比较多,所以有较为成熟的库:smart_open,相关代码如下:

import json
from smart_open import open

html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello from Lambda</title>
</head>
<body>
    <form>
        <input name="key" value="2025HH4B4WVC52WNSCGW" />
        <input name="url" />
        <input type="submit" />
    </form>
</body>
</html>
"""

def lambda_handler(event, context):

    # 获取查询字符串参数
    query_params = event.get('queryStringParameters', {})

    # 获取特定的参数值
    key = query_params.get('key')
    url = query_params.get('url')

    # 此处的 key 是为了避免陌生人的攻击
    defkey = "xxx"

    if defkey == key:

        if url == "":
             return {
                'statusCode': 200,
                "headers": {
                    "Content-Type": "text/html"  # 设置 Content-Type 为 text/html
                },
                'body': html_content,
            }
        else:
            ########################################
            # 核心代码就两句,请求 URL 内容,然后写入 S3
            ########################################
            with open(url, 'rb') as fin:
                with open('s3://$keyid:$secret@$bucket/$path', 'wb') as fout:
                    fout.write(fin.read())
                    # for line in fin:
                    #     fout.write(line)
    else:
        return {
            'statusCode': 200,
            'body': json.dumps('KEY ERROR!')
        }

    return {
        'statusCode': 200,
        'body': json.dumps('download from https://xxx.amazonaws.com/download!')
    }

需要注意的是,依赖要安装在当前目录,并和上诉代码一起打包上传

  • pip3 install smart_open -t ./
  • zip -r lambda.zip .

参考

后续发现 S3 的下载速度也不快,所以,又尝试了阿里云方案

阿里云方案

阿里云方案与 AWS 类似,只是换了 OSS,相关依赖也需要本地安装,并打包一起上传。

为了适配当前海外下载资源的场景,FC 和 OSS 都部署在新加坡。

// index.mjs
import OSSClient from 'ali-oss'
import stream from 'node:stream'

export const handler = async (event, context) => {
  console.log(event.toString())

  const query = JSON.parse(event.toString()).queryParameters;

  console.log('query', query)
  if (!query || !query.key || query.key != process.env.key) {
    return {
      message: 'key is required!'
    }
  }

  if (!query.url) {
    return {
      message: 'url is required!'
    }
  }

  var ossClient = new OSSClient({
    accessKeyId: process.env.accessKeyId,
    accessKeySecret: process.env.accessKeySecret,
    region: 'oss-ap-southeast-1',
    bucket: 'fc-download-proxy',
    // 设置secure为true,使用HTTPS,避免生成的下载链接被浏览器拦截
    secure: true,
    authorizationV4: true
  })

  try {
    const fileName = "download";

    // 下载并上传 oss
    const response = await fetch(
      query.url
    )
    const rs = stream.Readable.fromWeb(response.body)
    let result = await ossClient.putStream(fileName, rs)


    // OSS 是私有的,所以需要返回临时授权地址
    let downloadUrl = await ossClient.signatureUrlV4('GET', 3600, {
        headers: {} // 请根据实际发送的请求头设置此处的请求头
    }, fileName);

    return {
      downloadUrl
    }

  } catch (error) {
    return {
      message: 'error',
      error,
    }
  }
}

References