文件上传到github仓库

4/2/2024

# 思路

  1. 使用Github Pages的静态站点托管服务生成一个网站,这个网站只有简单的功能,上传文件到中转站。
  2. 这个中转站就是暂时存储文件,我们使用Leancloud的能力,提供开放api支持存储。
  3. 使用Github Actions能力,每日定时获取中转站的文件,然后上传到当前仓库下。

# 步骤

  1. 创建leancloud (opens new window)账号,然后创建一个应用,拿到appId和appKey。根据文档 (opens new window)可以看到,Leancloud提供了js的sdk,我们使用这个sdk来操作文件,实现思路中的步骤1和步骤2。示例如下
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>File Upload to Github</title>
    <script src="https://unpkg.com/leancloud-storage@4.15.2/dist/av-min.js"></script>
    <style>
      body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        margin: 20px;
        text-align: center;
      }

      h1 {
        color: #333;
      }

      .upload-container {
        margin-top: 30px;
        padding: 20px;
      }

      .custom-file-upload {
        display: inline-block;
        padding: 12px 24px;
        cursor: pointer;
        border-radius: 4px;
        background-color: #5cb85c;
        color: white;
        border: 1px solid transparent;
        transition: all 0.3s ease-in-out;
      }

      .custom-file-upload:hover {
        background-color: #4cae4c;
      }

      #file-input {
        display: none; /* Hide the default file input */
      }

      /* Responsive adjustments */
      @media (max-width: 768px) {
        .upload-container {
          padding: 15px;
        }
        .custom-file-upload {
          padding: 10px 20px; /* Slightly smaller padding on mobile */
        }
      }
    </style>
    <script>
      // 初始化 LeanCloud 应用
      AV.init({
        appId: "appId", // 替换为你的 App ID
        appKey: "appKey", // 替换为你的 App Key
        serverURL: "https://cn-n1-console-api.leancloud.cn", // 替换为你的 Server URL
      });

      // 文件上传函数
      function uploadFile(fileInput) {
        if (fileInput.files.length > 0) {
          // 遍历所有选择的文件
          Array.from(fileInput.files).forEach((file) => {
            // 检查文件大小(20MB)
            if (file.size <= 20 * 1024 * 1024) {
              // 创建 LeanCloud File 对象
              const leanFile = new AV.File(file.name, file);

              // 保存文件到 LeanCloud
              leanFile
                .save()
                .then((file) => {
                  console.log(`File uploaded successfully: ${file.url()}`);
                })
                .catch((error) => {
                  alert(`Error uploading file: ${error}`);
                });
            } else {
              alert(`${file.name} is too large. Limit is 20MB.`);
            }
          });
        }
      }

      // 手动触发文件选择框
      function triggerFileInput() {
        document.getElementById("file-input").click();
      }
    </script>
  </head>
  <body>
    <h1>上传至github仓库</h1>
    <div class="upload-container">
      <label for="file-input" class="custom-file-upload"> 选择文件 </label>
      <input type="file" id="file-input" onchange="uploadFile(this)" multiple />
    </div>
  </body>
</html>
  1. 创建一个js文件,用于读取leancloud中转站的文件,然后将文件上传到github仓库。

tips:需要在当前目录下创建downloaded文件夹

const fs = require('fs');
const https = require('https');
const { execSync } = require('child_process');
const AV = require("leancloud-storage");

// 初始化 LeanCloud 应用
AV.init({
  appId: "appId", // 替换为你的 App ID
  appKey: "appKey", // 替换为你的 App Key
  serverURL: "https://cn-n1-console-api.leancloud.cn", // 替换为你的 Server URL
});


// 辅助函数下载文件
const downloadFile = (url, outputPath) => {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(outputPath);
    https.get(url, (response) => {
      response.pipe(file);
      file.on('finish', () => {
        file.close(resolve);
      });
    }).on('error', (err) => {
      fs.unlink(outputPath); // 删除文件
      reject(err.message);
    });
  });
};

function getFiles() {
  // 获取文件
  return new Promise((resolve, reject) => {
    const query = new AV.Query('_File');
    query.find().then((nodes) => {
      const files = nodes.map(node => {
        return {
          url: node.get('url'),
          name: node.get('name')
        }
      });
      resolve(files);
    }).catch((error) => {
      reject(error);
      console.error('获取文件失败', error);
    });
  })
}

async function init() {
  const fileUrls = await getFiles();
  // 下载所有文件
  await Promise.all(
    fileUrls.map(({ url, name }) => {
      // 提取文件名,并定义本地下载路径
      const fileName = url.split('/').pop();
      const outputPath = `./downloaded/${name || fileName}`;
      const realUrl = url.replace('http://', 'https://');
      return downloadFile(realUrl, outputPath);
    })
  )
    .then(() => {
      // 所有文件下载完成后进行提交
      execSync('git config --local user.name "Zgrowth"');
      execSync('git config --local user.email "18296884762@163.com"');
      execSync('git add downloaded/*');
      execSync(`git commit -m "Add downloaded files ${new Date().getTime()}"`);
      execSync('git push');
      // 文件上传到仓库后将中转站的文件全部删除
      deleteClassData();
    })
    .catch((error) => {
      console.error('Error downloading files:', error);
    });
}

function deleteClassData() {
  const query = new AV.Query('_File');
  query.find().then((results) => {
    // 创建一个删除操作的数组
    const deletes = results.map(item => {
      return AV.Object.createWithoutData('_File', item.id).destroy();
    });

    // 并发执行所有删除操作
    return Promise.all(deletes);
  }).then(() => {
    console.log('全部数据已被删除。');
  }).catch((err) => {
    console.error('删除过程中出现错误:', err);
  });
}

init();

由于上面js文件使用到了leancloud-storage包,所以需要package.json

// package.json
{
  "name": "z-file",
  "version": "1.0.0",
  "description": "",
  "main": "download-and-commit.js",
  "scripts": {
    "start": "node ./download-and-commit.js"
  },
  "keywords": [],
  "author": "zs",
  "license": "ISC",
  "dependencies": {
    "leancloud-storage": "^4.15.2"
  }
}
  1. 使用github action 定时触发,并执行上述js文件
name: Download and Commit Files

on:
  workflow_dispatch:  # 手动触发
   # 定时器 github服务器时间比北京时间晚8小时 定时早上6点执行
  schedule: 
    - cron: '0 16 * * *'

jobs:
  download-and-commit:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          token: ${{ secrets.REPO_ACCESS_TOKEN }}

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: npm install
        run: npm install
    
      - name: Download files and commit
        run: npm start

# 解决没有足够的权限来推送文件到你的仓库

当 GitHub Actions 报告 "Permission to user/repo.git denied to github-actions[bot]" 错误时,这意味着尝试使用的 GitHub Token 没有足够的权限来推送到你的仓库。其中一个常见原因是尝试推送到一个受保护的分支,GitHub 默认的 GITHUB_TOKEN 没有权限执行这样的操作。

解决这个问题的方法之一是在仓库的 "Settings" -> "Secrets" 中添加一个有足够权限的 Personal Access Token (PAT),然后在你的 GitHub Actions 工作流中使用这个 PAT。

步骤如下:

1. 生成 Personal Access Token:

前往 GitHub -> Settings -> Developer settings -> Personal access tokens -> Generate new token。确保给予 token repo 范围的权限,以便它可以访问你的仓库。

2. 添加 PAT 到 GitHub 仓库的 Secrets:

前往仓库的 Settings -> Secrets -> New repository secret。添加一个名称(例如 REPO_ACCESS_TOKEN),粘贴你的 Personal Access Token。

3. 修改你的 GitHub Actions 工作流文件:

更新你的工作流文件(.github/workflows/your-workflow.yml),在相应步骤中使用这个新的 secret 来做身份验证。例如:

jobs:
  push-commit:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
        with:
          token: ${{ secrets.REPO_ACCESS_TOKEN }}  # 使用你的 Personal Access Token 代替默认的 GITHUB_TOKEN
          # 其他配置...

      # ... 其它步骤 ...

# 示例仓库

地址 (opens new window)