最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python結(jié)合PyQt5實現(xiàn)GitHub文件/文件夾批量上傳工具

 更新時間:2026年06月01日 08:38:27   作者:py小王子  
本文介紹了一個基于Python+PyQt5開發(fā)的GitHub文件上傳工具,支持可視化操作界面,無需命令行即可實現(xiàn)GitHub倉庫的自動創(chuàng)建和文件批量上傳,有需要的小伙伴可以了解下

基于 Python + PyQt5 開發(fā)的可視化桌面應用,核心功能是自動創(chuàng)建 GitHub 倉庫并支持單個文件整個文件夾的批量上傳,無需手動操作 Git 命令或 GitHub 網(wǎng)頁端,適合非技術(shù)人員或需要快速上傳文件到 GitHub 的場景。

核心特性

可視化操作界面,無需命令行;

自動檢測/創(chuàng)建 GitHub 倉庫(不存在則創(chuàng)建,存在則直接使用);

支持單文件/整文件夾上傳,保留文件夾原有目錄結(jié)構(gòu);

實時上傳進度顯示 + 詳細日志輸出,便于排查問題;

支持文件覆蓋(自動獲取文件 SHA 值實現(xiàn)更新);

自定義倉庫內(nèi)上傳根路徑(如 uploads/)。

二、環(huán)境準備

1. 依賴安裝

工具基于 Python 開發(fā),需先安裝以下依賴包:

pip install requests PyQt5

2. GitHub 個人訪問令牌(PAT)準備

上傳文件需要 GitHub 權(quán)限,需創(chuàng)建帶 repo 權(quán)限的經(jīng)典令牌:

  • 打開 GitHub 設置
  • 點擊「Generate new token (classic)」;
  • 勾選 repo 權(quán)限(倉庫相關(guān)操作);
  • 設置令牌有效期(建議選「No expiration」,或按需設置);
  • 生成令牌后立即復制保存(僅顯示一次,丟失需重新創(chuàng)建)。

三、工具使用教程

1. 運行程序

將代碼保存為 github_uploader.py,執(zhí)行以下命令啟動:

python github_uploader.py

啟動后會看到可視化主界面,分為 5 個核心區(qū)域:

2. 操作步驟

步驟 1:選擇上傳模式

  • 「單個文件」:上傳指定的單個文件(如 test.txt);
  • 「整個文件夾」:上傳文件夾內(nèi)所有文件(保留目錄結(jié)構(gòu))。

步驟 2:選擇本地路徑

  • 點擊「選擇文件/選擇文件夾」按鈕,選擇要上傳的本地文件/文件夾;
  • 路徑會自動填充到輸入框中。

步驟 3:填寫 GitHub 配置

配置項說明
倉庫所有者你的 GitHub 用戶名(區(qū)分大小寫,如 octocat
倉庫名稱要創(chuàng)建/上傳的倉庫名(如 file-upload-test,不存在則自動創(chuàng)建)
PAT 令牌步驟 2 中創(chuàng)建的帶 repo 權(quán)限的令牌(輸入時隱藏,保障安全)
倉庫內(nèi)根路徑可選,文件上傳到倉庫的指定目錄(默認 uploads/,如填 docs/ 則文件上傳到 docs/ 下)

步驟 4:開始上傳

點擊「開始上傳」按鈕,程序會:

  • 校驗輸入是否完整(路徑、用戶名、倉庫名、令牌不能為空);
  • 檢查倉庫是否存在,不存在則自動創(chuàng)建;
  • 掃描本地文件,批量上傳(實時輸出日志 + 進度);

「清空日志」按鈕可清空當前日志區(qū)域內(nèi)容,便于重新上傳。

3. 上傳結(jié)果說明

  • 上傳成功:彈窗提示「上傳完成!所有文件均成功上傳到 GitHub」,日志顯示每個文件的上傳狀態(tài);
  • 部分失?。簭棿疤崾尽干蟼魍瓿?,但有步驟失敗」,需查看日志中的錯誤信息(如令牌權(quán)限不足、文件路徑錯誤等)。

四、核心代碼解析

1. 核心類說明

類名作用
UploadThread上傳線程類(繼承 QThread),避免 UI 卡頓,包含倉庫創(chuàng)建、文件掃描、文件上傳邏輯
GitHubUploader主界面類(繼承 QWidget),負責界面布局、用戶交互、線程調(diào)用

2. 關(guān)鍵功能實現(xiàn)

(1)自動創(chuàng)建倉庫

def create_github_repo(self):
    # 檢查倉庫是否存在
    check_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}"
    check_response = requests.get(check_url, headers=headers)
    # 不存在則創(chuàng)建
    if check_response.status_code != 200:
        create_url = "https://api.github.com/user/repos"
        create_data = {"name": self.repo_name, "private": False, "auto_init": True}
        requests.post(create_url, json=create_data, headers=headers)
  • 通過 GitHub API 先檢查倉庫是否存在,狀態(tài)碼 200 表示存在;
  • 不存在則調(diào)用創(chuàng)建倉庫 API,auto_init=True 自動初始化倉庫(避免空倉庫無法上傳)。

(2)文件上傳核心邏輯

def upload_single_file(self, local_file_path, github_file_path):
    # 讀取文件并 Base64 編碼(GitHub API 要求)
    with open(local_file_path, "rb") as f:
        file_content = base64.b64encode(f.read()).decode("utf-8")
    # 構(gòu)建上傳請求(PUT 方法,支持覆蓋)
    api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/contents/{github_file_path}"
    data = {"message": f"Upload file: {os.path.basename(local_file_path)}", "content": file_content}
    # 若文件已存在,添加 SHA 值實現(xiàn)覆蓋
    if response.status_code == 200:
        data["sha"] = response.json()["sha"]
    requests.put(api_url, json=data, headers=headers)
  • GitHub API 要求文件內(nèi)容為 Base64 編碼;
  • 使用 PUT 方法上傳,已存在的文件需傳入 SHA 值實現(xiàn)覆蓋。

(3)多線程處理

# 線程信號定義(用于線程與 UI 通信)
log_signal = pyqtSignal(str)  # 日志更新
progress_signal = pyqtSignal(int)  # 進度更新
finish_signal = pyqtSignal(bool)  # 上傳完成
  • 上傳邏輯放在獨立線程中,避免 UI 凍結(jié);
  • 通過 pyqtSignal 實現(xiàn)線程向主界面?zhèn)鬟f日志、進度等信息。

五、常見問題排查

1. 倉庫創(chuàng)建失敗

  • 原因 1:PAT 令牌無 repo 權(quán)限 → 重新創(chuàng)建令牌并勾選 repo;
  • 原因 2:倉庫名已被占用 → 更換倉庫名;
  • 原因 3:網(wǎng)絡問題 → 檢查網(wǎng)絡連接,或更換 GitHub 鏡像源。

2. 文件上傳失敗

  • 原因 1:本地文件路徑錯誤 → 確認文件/文件夾存在;
  • 原因 2:令牌過期/錯誤 → 檢查令牌是否正確,或重新生成;
  • 原因 3:文件過大 → GitHub API 單文件上傳限制 100MB,超大文件需用 Git LFS(工具暫不支持)。

3. UI 無響應

原因:上傳超大文件夾時,線程未及時釋放資源 → 等待日志輸出,或拆分文件夾分批上傳。

七、完整代碼(可直接復制)

import sys
import requests
import base64
import os
from pathlib import Path
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QLineEdit, QPushButton, QFileDialog,
    QTextEdit, QGroupBox, QMessageBox, QRadioButton
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal


# 上傳線程(支持自動創(chuàng)建倉庫 + 文件/文件夾上傳)
class UploadThread(QThread):
    log_signal = pyqtSignal(str)  # 日志信號
    progress_signal = pyqtSignal(int)  # 進度信號
    finish_signal = pyqtSignal(bool)  # 完成信號

    def __init__(self, target_path, repo_owner, repo_name, github_token, github_root_path, is_folder):
        super().__init__()
        self.target_path = target_path  # 本地文件/文件夾路徑
        self.repo_owner = repo_owner  # GitHub倉庫所有者
        self.repo_name = repo_name  # GitHub倉庫名
        self.github_token = github_token  # PAT令牌
        self.github_root_path = github_root_path  # 倉庫內(nèi)根路徑
        self.is_folder = is_folder  # 是否是文件夾上傳
        self.total_files = 0  # 總文件數(shù)
        self.uploaded_files = 0  # 已上傳文件數(shù)

    # 自動創(chuàng)建GitHub倉庫(不存在則創(chuàng)建)
    def create_github_repo(self):
        try:
            # 1. 先檢查倉庫是否已存在
            check_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}"
            headers = {
                "Authorization": f"token {self.github_token}",
                "Accept": "application/vnd.github.v3+json"
            }
            check_response = requests.get(check_url, headers=headers)

            # 倉庫已存在
            if check_response.status_code == 200:
                self.log_signal.emit(f"倉庫 {self.repo_owner}/{self.repo_name} 已存在,無需創(chuàng)建")
                return True

            # 倉庫不存在,嘗試創(chuàng)建
            self.log_signal.emit(f"倉庫 {self.repo_owner}/{self.repo_name} 不存在,正在自動創(chuàng)建...")
            create_url = "https://api.github.com/user/repos"
            create_data = {
                "name": self.repo_name,
                "private": False,  # 創(chuàng)建公共倉庫(如需私有,改為True)
                "auto_init": True,  # 自動初始化(創(chuàng)建README,避免空倉庫)
                "description": "Auto-created by GitHub Upload Tool"
            }
            create_response = requests.post(create_url, json=create_data, headers=headers)

            if create_response.status_code == 201:
                self.log_signal.emit(f"倉庫 {self.repo_name} 創(chuàng)建成功!")
                return True
            else:
                error_detail = create_response.json() if create_response.headers.get(
                    'Content-Type') == 'application/json' else create_response.text
                self.log_signal.emit(f"倉庫創(chuàng)建失敗: {error_detail}")
                return False
        except Exception as e:
            self.log_signal.emit(f"倉庫檢查/創(chuàng)建出錯: {str(e)}")
            return False

    # 遞歸獲取文件夾內(nèi)所有文件
    def get_all_files(self, folder_path):
        file_list = []
        for root, dirs, files in os.walk(folder_path):
            for file in files:
                file_path = os.path.join(root, file)
                # 計算相對路徑(保持文件夾結(jié)構(gòu))
                rel_path = os.path.relpath(file_path, folder_path)
                file_list.append((file_path, rel_path))
        self.total_files = len(file_list)
        return file_list

    # 單個文件上傳邏輯
    def upload_single_file(self, local_file_path, github_file_path):
        try:
            # 讀取并編碼文件內(nèi)容
            with open(local_file_path, "rb") as f:
                file_content = f.read()
            encoded_content = base64.b64encode(file_content).decode("utf-8")

            # 構(gòu)建API請求URL
            api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/contents/{github_file_path}"
            headers = {
                "Authorization": f"token {self.github_token}",
                "Accept": "application/vnd.github.v3+json"
            }

            # 檢查文件是否已存在(獲取SHA)
            response = requests.get(api_url, headers=headers)
            data = {
                "message": f"Upload file: {os.path.basename(local_file_path)}",
                "content": encoded_content
            }
            if response.status_code == 200:
                data["sha"] = response.json()["sha"]

            # 發(fā)送上傳請求
            response = requests.put(api_url, json=data, headers=headers)
            if response.status_code in [200, 201]:
                self.uploaded_files += 1
                progress = int(self.uploaded_files / self.total_files * 100) if self.total_files > 0 else 100
                self.progress_signal.emit(progress)
                return True, f"上傳成功: {github_file_path}"
            else:
                error_detail = response.json() if response.headers.get(
                    'Content-Type') == 'application/json' else response.text
                return False, f"上傳失敗: {github_file_path} (狀態(tài)碼: {response.status_code})\n   錯誤詳情: {error_detail}"
        except Exception as e:
            return False, f"文件出錯: {local_file_path} - {str(e)}"

    def run(self):
        try:
            # 第一步:檢查/創(chuàng)建倉庫(核心新增邏輯)
            self.log_signal.emit("開始檢查倉庫狀態(tài)...")
            if not self.create_github_repo():
                self.finish_signal.emit(False)
                return

            # 第二步:掃描文件
            self.log_signal.emit("開始掃描本地文件...")

            # 區(qū)分文件/文件夾處理
            if self.is_folder:
                # 文件夾上傳:遞歸獲取所有文件
                if not os.path.isdir(self.target_path):
                    raise NotADirectoryError(f"指定路徑不是文件夾: {self.target_path}")

                file_list = self.get_all_files(self.target_path)
                if self.total_files == 0:
                    self.log_signal.emit("文件夾內(nèi)未找到任何文件")
                    self.finish_signal.emit(True)
                    return

                self.log_signal.emit(f"共檢測到 {self.total_files} 個文件,開始批量上傳...")

                # 逐個上傳文件
                for local_file, rel_path in file_list:
                    # 拼接倉庫內(nèi)完整路徑(根路徑 + 相對路徑)
                    github_file_path = os.path.join(self.github_root_path, rel_path).replace("\\", "/")
                    success, msg = self.upload_single_file(local_file, github_file_path)
                    self.log_signal.emit(msg)

                    # 如果上傳失敗,記錄但繼續(xù)上傳其他文件
                    if not success:
                        self.log_signal.emit(f"繼續(xù)上傳其他文件...")

                self.log_signal.emit(f"\n上傳完成!總計 {self.total_files} 個文件,成功上傳 {self.uploaded_files} 個")
                self.finish_signal.emit(self.uploaded_files == self.total_files)

            else:
                # 單個文件上傳
                self.total_files = 1
                self.progress_signal.emit(0)
                github_file_path = os.path.join(self.github_root_path, os.path.basename(self.target_path)).replace("\\",
                                                                                                                   "/")
                success, msg = self.upload_single_file(self.target_path, github_file_path)
                self.log_signal.emit(msg)
                self.progress_signal.emit(100)
                self.finish_signal.emit(success)

        except Exception as e:
            self.log_signal.emit(f"整體流程出錯: {str(e)}")
            self.finish_signal.emit(False)


# 主界面窗口
class GitHubUploader(QWidget):
    def __init__(self):
        super().__init__()
        self.upload_thread = None
        self.is_folder_mode = False  # 默認是文件模式
        self.init_ui()

    def init_ui(self):
        # 窗口基本設置
        self.setWindowTitle("GitHub 文件/文件夾上傳工具")
        self.setGeometry(100, 100, 800, 600)

        # 整體布局
        main_layout = QVBoxLayout()
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        # 1. 選擇模式(文件/文件夾)
        mode_group = QGroupBox("上傳模式")
        mode_layout = QHBoxLayout()

        self.file_radio = QRadioButton("單個文件")
        self.folder_radio = QRadioButton("整個文件夾")
        self.file_radio.setChecked(True)  # 默認選中文件模式

        # 綁定模式切換事件
        self.file_radio.clicked.connect(self.switch_mode)
        self.folder_radio.clicked.connect(self.switch_mode)

        mode_layout.addWidget(self.file_radio)
        mode_layout.addWidget(self.folder_radio)
        mode_group.setLayout(mode_layout)
        main_layout.addWidget(mode_group)

        # 2. 路徑選擇區(qū)域
        path_group = QGroupBox("路徑選擇")
        path_layout = QHBoxLayout()
        self.path_edit = QLineEdit()
        self.path_edit.setPlaceholderText("請選擇要上傳的文件/文件夾路徑")
        self.path_btn = QPushButton("選擇文件")
        self.path_btn.clicked.connect(self.select_path)
        path_layout.addWidget(self.path_edit)
        path_layout.addWidget(self.path_btn)
        path_group.setLayout(path_layout)
        main_layout.addWidget(path_group)

        # 3. GitHub配置區(qū)域
        github_group = QGroupBox("GitHub 配置")
        github_layout = QVBoxLayout()

        # 倉庫所有者
        owner_layout = QHBoxLayout()
        owner_layout.addWidget(QLabel("倉庫所有者:"))
        self.repo_owner_edit = QLineEdit()
        self.repo_owner_edit.setPlaceholderText("必填:你的GitHub用戶名(區(qū)分大小寫)")
        owner_layout.addWidget(self.repo_owner_edit)
        github_layout.addLayout(owner_layout)

        # 倉庫名稱
        repo_layout = QHBoxLayout()
        repo_layout.addWidget(QLabel("倉庫名稱:"))
        self.repo_name_edit = QLineEdit()
        self.repo_name_edit.setPlaceholderText("必填:要創(chuàng)建/上傳的倉庫名(如test-upload)")
        repo_layout.addWidget(self.repo_name_edit)
        github_layout.addLayout(repo_layout)

        # 訪問令牌
        token_layout = QHBoxLayout()
        token_layout.addWidget(QLabel("PAT令牌:"))
        self.token_edit = QLineEdit()
        self.token_edit.setPlaceholderText("必填:帶repo權(quán)限的GitHub經(jīng)典令牌")
        self.token_edit.setEchoMode(QLineEdit.Password)
        token_layout.addWidget(self.token_edit)
        github_layout.addLayout(token_layout)

        # 倉庫內(nèi)根路徑
        root_layout = QHBoxLayout()
        root_layout.addWidget(QLabel("倉庫內(nèi)根路徑:"))
        self.github_root_edit = QLineEdit()
        self.github_root_edit.setPlaceholderText("可選:如uploads/(默認自動填充)")
        self.github_root_edit.setText("uploads/")  # 默認根路徑
        root_layout.addWidget(self.github_root_edit)
        github_layout.addLayout(root_layout)

        github_group.setLayout(github_layout)
        main_layout.addWidget(github_group)

        # 4. 操作按鈕 + 進度提示
        op_layout = QHBoxLayout()
        self.upload_btn = QPushButton("開始上傳")
        self.upload_btn.clicked.connect(self.start_upload)
        self.clear_btn = QPushButton("清空日志")
        self.clear_btn.clicked.connect(self.clear_log)
        self.progress_label = QLabel("進度: 0%")
        op_layout.addWidget(self.upload_btn)
        op_layout.addWidget(self.clear_btn)
        op_layout.addStretch()
        op_layout.addWidget(self.progress_label)
        main_layout.addLayout(op_layout)

        # 5. 日志輸出區(qū)域
        log_group = QGroupBox("上傳日志")
        log_layout = QVBoxLayout()
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)

        self.setLayout(main_layout)

    # 切換上傳模式(文件/文件夾)
    def switch_mode(self):
        self.is_folder_mode = self.folder_radio.isChecked()
        if self.is_folder_mode:
            self.path_btn.setText("選擇文件夾")
            self.path_edit.setPlaceholderText("請選擇要上傳的文件夾路徑")
        else:
            self.path_btn.setText("選擇文件")
            self.path_edit.setPlaceholderText("請選擇要上傳的文件路徑")

    # 選擇文件/文件夾路徑
    def select_path(self):
        if self.is_folder_mode:
            # 選擇文件夾
            folder_path = QFileDialog.getExistingDirectory(self, "選擇文件夾")
            if folder_path:
                self.path_edit.setText(folder_path)
        else:
            # 選擇文件
            file_path, _ = QFileDialog.getOpenFileName(self, "選擇文件", "", "所有文件 (*.*)")
            if file_path:
                self.path_edit.setText(file_path)

    # 清空日志
    def clear_log(self):
        self.log_text.clear()
        self.progress_label.setText("進度: 0%")

    # 開始上傳
    def start_upload(self):
        # 1. 校驗輸入
        target_path = self.path_edit.text().strip()
        repo_owner = self.repo_owner_edit.text().strip()
        repo_name = self.repo_name_edit.text().strip()
        token = self.token_edit.text().strip()
        github_root_path = self.github_root_edit.text().strip()

        if not target_path:
            tip = "請先選擇要上傳的文件夾!" if self.is_folder_mode else "請先選擇要上傳的文件!"
            QMessageBox.warning(self, "提示", tip)
            return
        if not repo_owner or not repo_name or not token:
            QMessageBox.warning(self, "提示", "倉庫所有者、倉庫名稱、PAT令牌均為必填項!")
            return

        # 2. 初始化狀態(tài)
        self.upload_btn.setEnabled(False)
        self.clear_log()
        self.log_text.append("?? 開始執(zhí)行上傳流程...")

        # 3. 創(chuàng)建并啟動上傳線程
        self.upload_thread = UploadThread(
            target_path=target_path,
            repo_owner=repo_owner,
            repo_name=repo_name,
            github_token=token,
            github_root_path=github_root_path,
            is_folder=self.is_folder_mode
        )
        self.upload_thread.log_signal.connect(self.append_log)
        self.upload_thread.progress_signal.connect(self.update_progress)
        self.upload_thread.finish_signal.connect(self.upload_finish)
        self.upload_thread.start()

    # 追加日志
    def append_log(self, msg):
        self.log_text.append(msg)
        # 自動滾動到最新日志
        self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())

    # 更新進度
    def update_progress(self, progress):
        self.progress_label.setText(f"進度: {progress}%")

    # 上傳完成
    def upload_finish(self, is_success):
        self.upload_btn.setEnabled(True)
        if is_success:
            QMessageBox.information(self, "成功", "上傳完成!所有文件均成功上傳到GitHub")
        else:
            QMessageBox.warning(self, "部分失敗", "上傳完成,但有步驟失敗,請查看日志詳情!")


# 程序入口
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = GitHubUploader()
    window.show()
    sys.exit(app.exec_())

總結(jié)

  1. 該工具核心是通過 GitHub API 實現(xiàn)倉庫自動創(chuàng)建和文件上傳,結(jié)合 PyQt5 實現(xiàn)可視化操作;
  2. 使用前需準備帶 repo 權(quán)限的 GitHub PAT 令牌,確保依賴包安裝完整;
  3. 支持單文件/文件夾上傳,保留目錄結(jié)構(gòu),實時輸出日志和進度,便于排查問題;
  4. 可通過 pyinstaller 打包為 exe 文件,提升易用性,也可擴展私有倉庫、斷點續(xù)傳等功能。

到此這篇關(guān)于Python結(jié)合PyQt5實現(xiàn)GitHub文件/文件夾批量上傳工具的文章就介紹到這了,更多相關(guān)Python GitHub文件上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

娄底市| 郯城县| 鸡泽县| 马鞍山市| 中阳县| 莫力| 德化县| 阿坝县| 上林县| 那曲县| 苗栗县| 武川县| 于田县| 西吉县| 拉萨市| 龙南县| 泰安市| 叙永县| 九台市| 岢岚县| 石狮市| 洛扎县| 龙川县| 明星| 江孜县| 邳州市| 石棉县| 新宾| 涞水县| 河曲县| 新田县| 温州市| 桑日县| 重庆市| 共和县| 图们市| 贵港市| 大兴区| 鹿泉市| 嵊泗县| 广汉市|