Web Api接品代碼:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Concurrent;
using System.Net;
namespace LargeFileHandling.Controllers
{
[ApiController]
[Route("[controller]")]
public class TestProgramFileManagerController : Controller
{
private readonly string _testProgramPath;
// 用于存儲文件塊的信息
private static ConcurrentDictionary<string, int> fileChunkTracker = new ConcurrentDictionary<string, int>();
public TestProgramFileManagerController(FileStorageSettings fileStorageSettings)
{
_testProgramPath = fileStorageSettings.TestProgramPath;
//確保上傳文件夾存在
if (!Directory.Exists(_testProgramPath))
{
Directory.CreateDirectory(_testProgramPath);//如果不存在則創(chuàng)建
}
}
//上傳測試日志
[HttpPost("uploadTestProgram")]
/*
testProgramFile:測試程式文件
ProgramDriectory:程式存放目錄
*/
public async Task<IActionResult> UploadTestProgram(IFormFile testProgramFile, string programDriectory)
{
try
{
//判斷產(chǎn)品文件夾是否存在
if (!Directory.Exists($@"{_testProgramPath}\{programDriectory}"))
{
Directory.CreateDirectory($@"{_testProgramPath}\{programDriectory}");//如果不存在則創(chuàng)建
}
if (testProgramFile == null || testProgramFile.Length == 0)
{
return BadRequest(new { code = 400, message = "Upload failed. No file provided.", data = new { } });
}
var filename = Path.GetFileName(testProgramFile.FileName);
var filePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", filename);
await using (var stream = System.IO.File.Create(filePath))
{
await testProgramFile.CopyToAsync(stream);
}
return Ok(new { code = 200, message = "test log file uploaded successfully.", data = new { filePath } });
}
catch (Exception ex)
{
// 增加更詳細的錯誤日志
Console.WriteLine($"Error: {ex.GetType().FullName}: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
// 記錄到日志文件或其他監(jiān)控工具(根據(jù)您的日志配置)
// Logger.LogError(ex, "Error listing files");
return StatusCode(500, new { code = 500, message = "UploadTestProgram Error.", details = ex.Message });
}
}
// Check if file exists and return JSON response
/*
* 查找測試程式文件是否存在
testProgramFile:測試程式文件
programDriectory:程式目錄
*/
[HttpGet("exists")]
public IActionResult CheckProgramFileExists(string testProgramFile, string programDriectory)
{
var filePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", testProgramFile);
if (System.IO.File.Exists(filePath))
{
return Ok(new { code = 200, message = "File exists.", data = new { filePath } });
}
else
{
return NotFound(new { code = 404, message = "File not found.", data = new { } });
}
}
// Actual file download operation
/*
下載測試程式
testProgramFile:程式文件
programDriectory:程式目錄
*/
[HttpGet("download")]
public IActionResult DownloadTestProgramFile(string testProgramFile, string programDriectory)
{
try
{
//var filePath = Path.Combine(_uploadFolderPath, fileName);
// 解碼路徑
string decodedPath = WebUtility.UrlDecode($@"{_testProgramPath}\{programDriectory}\{testProgramFile}");
if (!System.IO.File.Exists(decodedPath))
{
return NotFound(new { code = 404, message = "File not found.", data = new { } });
}
var stream = new FileStream(decodedPath, FileMode.Open, FileAccess.Read);
return new FileStreamResult(stream, "application/octet-stream")
{
FileDownloadName = testProgramFile
};
}
catch (Exception ex)
{
// 增加更詳細的錯誤日志
Console.WriteLine($"Error: {ex.GetType().FullName}: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
// 記錄到日志文件或其他監(jiān)控工具(根據(jù)您的日志配置)
// Logger.LogError(ex, "Error listing files");
return StatusCode(500, new { code = 500, message = "Internal Server Error.", details = ex.Message });
}
}
/// <summary>
/// 上傳測試程序塊
/// </summary>
/// <param name="testProgramFile">測試程式文件</param>
/// <param name="programDriectory">文件上傳目錄</param>
/// <param name="fileName">文件名</param>
/// <param name="chunkIndex">文件對應(yīng)塊指針</param>
/// <param name="totalChunks">合總塊</param>
/// <returns></returns>
[HttpPost("uploadtestProgramChunk")]
public async Task<IActionResult> UploadtestProgramChunk(IFormFile testProgramFile, string programDriectory, string fileName, int chunkIndex, int totalChunks)
{
if (testProgramFile == null || testProgramFile.Length == 0)
{
return BadRequest(new { code = 400, message = "Upload failed. No chunk provided." });
}
string tempFilePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", $"{fileName}.part{chunkIndex}");
await using (var fileStream = System.IO.File.Create(tempFilePath))
{
await testProgramFile.CopyToAsync(fileStream);
}
fileChunkTracker.AddOrUpdate(fileName, 1, (key, oldValue) => oldValue + 1);
if (fileChunkTracker[fileName] == totalChunks)
{
bool mergeSuccess = await MergeFileChunks(fileName, totalChunks, programDriectory);
if (!mergeSuccess)
{
return StatusCode(500, new { code = 500, message = "Error merging file chunks." });
}
fileChunkTracker.TryRemove(fileName, out _);
return Ok(new { code = 200, message = "File uploaded and merged successfully." });
}
return Ok(new { code = 200, message = "Chunk uploaded successfully." });
}
/// <summary>
/// 合并文件塊
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="totalChunks">總塊數(shù)</param>
/// <param name="programDriectory">程式目錄</param>
/// <returns></returns>
private async Task<bool> MergeFileChunks(string fileName, int totalChunks, string programDriectory)
{
string finalFilePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", fileName);
try
{
// Check if all chunk files exist before starting to merge
for (int i = 0; i < totalChunks; i++)
{
string tempFilePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", $"{fileName}.part{i}");
if (!System.IO.File.Exists(tempFilePath))
{
Console.WriteLine($"Missing chunk: {tempFilePath}");
return false; // If any chunk is missing, abort the merge
}
}
await using (var finalStream = new FileStream(finalFilePath, FileMode.Create))
{
for (int i = 0; i < totalChunks; i++)
{
string tempFilePath = Path.Combine($@"{_testProgramPath}\{programDriectory}", $"{fileName}.part{i}");
await using (var sourceStream = new FileStream(tempFilePath, FileMode.Open))
{
await sourceStream.CopyToAsync(finalStream);
}
System.IO.File.Delete(tempFilePath); // Delete the chunk file after it's been merged
}
}
return true; // All chunks have been merged successfully
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while merging chunks: {ex.Message}");
return false;
}
}
/// <summary>
/// 合并單元
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="totalChunks">總塊數(shù)</param>
/// <param name="programDriectory">程式目錄</param>
/// <returns></returns>
[HttpPost("mergeChunks")]
public async Task<IActionResult> MergeChunks(string fileName, int totalChunks, string programDriectory)
{
bool mergeSuccess = await MergeFileChunks(fileName, totalChunks, programDriectory);
if (!mergeSuccess)
{
return StatusCode(500, new { code = 500, message = "Error merging file chunks." });
}
return Ok(new { code = 200, message = "File merged successfully." });
}
/// <summary>
/// 列出下載程式清單
/// </summary>
/// <param name="programDriectory">程式目錄</param>
/// <returns></returns>
[HttpGet("listdownFilePath")]
public IActionResult ListDownloadableTestProgramFiles(string programDriectory)
{
try
{
// 解碼路徑
string decodedPath = WebUtility.UrlDecode($@"{_testProgramPath}\{programDriectory}");
if (!Directory.Exists(decodedPath))
{
return new JsonResult(new { code = 404, message = "Upload folder not found." }) { StatusCode = StatusCodes.Status404NotFound };
}
var files = Directory.GetFiles(decodedPath, "*.*", SearchOption.AllDirectories)
.Select(file => file.Replace(decodedPath, "").TrimStart(Path.DirectorySeparatorChar))
.ToList();
return new JsonResult(new { code = 200, message = "File list retrieved successfully.", data = new { files } }) { StatusCode = StatusCodes.Status200OK };
}
catch (Exception ex)
{
// 增加更詳細的錯誤日志
Console.WriteLine($"Error: {ex.GetType().FullName}: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
// 記錄到日志文件或其他監(jiān)控工具(根據(jù)您的日志配置)
// Logger.LogError(ex, "Error listing files");
return StatusCode(500, new { code = 500, message = "Internal Server Error.", details = ex.Message });
}
}
}
}
?文章來源:http://www.zghlxwxcb.cn/news/detail-847513.html
import configparser
import logging
import sys
import time # 修改這里
#import time as time_module # 修改這里,避免命名沖突
import requests
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QProgressBar, QLabel, QDesktopWidget, \
QMessageBox
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QPoint
import os
import platform
import subprocess
class DownloadThread(QThread):
progress_updated = pyqtSignal(int)
speed_updated = pyqtSignal(str)
download_completed = pyqtSignal() # 新信號,表示下載完成
current_file = pyqtSignal(str) # 新增信號,傳遞當(dāng)前下載文件的名稱
# 其他信號保持不變
error_occurred = pyqtSignal() # 新增信號,表示發(fā)生了錯誤
def __init__(self, url, params_list, save_path_prefix):
super().__init__()
self.url = url
self.params_list = params_list # 現(xiàn)在是參數(shù)列表,每個元素是一個文件的參數(shù)
self.save_path_prefix = save_path_prefix.replace("\\", "/") # 確保使用正斜杠
#執(zhí)行
def run(self):
for params in self.params_list:
# 用于Windows路徑和兼容UNIX-like系統(tǒng)的路徑處理
file_path = params['testProgramFile'].replace("\\", "/")
self.current_file.emit(file_path) # 發(fā)出當(dāng)前下載文件的名稱
# 檢查文件路徑中是否包含目錄
if "/" in file_path:
# 存在目錄,創(chuàng)建目錄結(jié)構(gòu)
full_path = os.path.join(self.save_path_prefix, file_path)
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
os.makedirs(directory, exist_ok=True)
else:
# 文件不包含目錄,直接保存到根目錄下
full_path = os.path.join(self.save_path_prefix, file_path)
try:
with requests.get(self.url, params=params, stream=True) as r:
r.raise_for_status()
total_length = int(r.headers.get('content-length', 0))
download_length = 0
start_time = time.time()
with open(full_path, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
download_length += len(chunk)
if total_length > 0:
progress = int(100 * download_length / total_length)
self.progress_updated.emit(progress) # 發(fā)出進度更新信號
elapsed_time = time.time() - start_time
if elapsed_time > 0:
speed = download_length / elapsed_time
self.speed_updated.emit(f"{speed / 1024:.2f} KB/s") # 發(fā)出速度更新信號
except Exception as e:
print(f"下載{file_path}失敗: {e}")
self.error_occurred.emit()
self.download_completed.emit()
# 檢查操作系統(tǒng),如果不是Windows,則執(zhí)行chmod命令
print(platform.system())
if platform.system() != "Windows":
try:
# 構(gòu)建命令字符串
cmd = f"chmod 777 {self.save_path_prefix} -R"
subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"修改權(quán)限失敗: {e}")
# def run(self):
# try:
# for params in self.params_list:
# file_name = params['testProgramFile']
# self.current_file.emit(file_name) # 發(fā)出當(dāng)前文件名信號
# save_path = f"{self.save_path_prefix}{file_name}"
# try:
# with requests.get(self.url, params=params, stream=True) as r:
# r.raise_for_status() # 確保請求成功
# total_length = int(r.headers.get('content-length'))
# download_length = 0
# start_time = time.time()
# with open(save_path, 'wb') as f:
# for chunk in r.iter_content(chunk_size=1024):
# if chunk:
# f.write(chunk)
# download_length += len(chunk)
# progress = int(100 * download_length / total_length)
# self.progress_updated.emit(progress)
#
# elapsed_time = time.time() - start_time
# if elapsed_time > 0:
# speed = download_length / elapsed_time
# self.speed_updated.emit(f"{speed / 1024:.2f} KB/s")
# except Exception as e:
# print(f"下載{file_name}失敗: {e}")
#
# self.download_completed.emit() # 下載完成后發(fā)出信號
#
# except Exception as e:
# print(f"下載過程中發(fā)生錯誤: {e}")
# self.error_occurred.emit()
class MainWindow(QWidget):
# 定義顏色代碼
COLOR_SUCCESS = "\033[1;32;43m"
COLOR_ERROR = "\033[1;31m"
COLOR_RESET = "\033[0m"
def __init__(self):
super().__init__()
self.initUI()
self.start_download() # 添加這行代碼來在窗口初始化后自動開始下載
self.config = configparser.ConfigParser()
# 生成日志信息
self.logger = logging.getLogger('my_logger') # 步驟1 創(chuàng)建日志記錄器
self.logger.setLevel(logging.DEBUG) # 步驟2 將指定日志級別
self.file_handler = logging.FileHandler('log/log.txt') # 步驟3 創(chuàng)建文件處理器
self.formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') # 步驟4 創(chuàng)建格式化器
self.file_handler.setFormatter(self.formatter) # 步驟4 將格式化器添加到處理器
self.logger.addHandler(self.file_handler) # 步驟5 將處理器添加到日志記錄器
# 讀取配置
self.config.read('./Conf/config.conf', encoding='utf-8') # 讀取配置文件,如果配置文件不存在則創(chuàng)建
# 讀取Test_WebApi接口配置
self.TestWebApi = self.config.get('TestWepApi', 'url')
def initUI(self):
self.setWindowTitle('File Downloader')
self.setGeometry(100, 100, 400, 200)
self.setWindowFlags(Qt.FramelessWindowHint) # 設(shè)置為無邊框窗口
self.layout = QVBoxLayout()
self.progress_bar = QProgressBar(self)
self.speed_label = QLabel('Speed: 0 KB/s', self)
self.current_file_label = QLabel('Current File: None', self) # 新增標(biāo)簽
#self.download_button = QPushButton('Download', self)
#self.download_button.clicked.connect(self.start_download)
self.layout.addWidget(self.progress_bar)
self.layout.addWidget(self.speed_label)
self.layout.addWidget(self.current_file_label) # 將新標(biāo)簽添加到布局
#self.layout.addWidget(self.download_button)
self.setLayout(self.layout)
self.centerWindow()#窗體居中顯示
self.oldPos = self.pos()
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint(event.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
#獲取文件列表
def get_file_list(self):
url = f'{self.TestWebApi}/TestProgramFileManager/listdownFilePath'
params = {'programDriectory': 'Online_UpdateProgram\\X86_UOS'}
try:
response = requests.get(url, params=params)
response.raise_for_status() # 這將拋出異常,如果請求返回了一個錯誤
file_list = response.json().get('data', {}).get('files', [])
return file_list
except requests.RequestException as e:
#print(f"獲取文件列表失敗: {e}")
self.ShowLog(f"獲取文件列表失敗: {e}",False)
return []
#執(zhí)行文件下載
# MainWindow中start_download方法的修改
def start_download(self):
try:
file_list = self.get_file_list()
if not file_list:
#print("文件列表為空,無法下載")
self.ShowLog("文件列表為空,無法下載",False)
return
params_list = [{'testProgramFile': file_name, 'programDriectory': 'Online_UpdateProgram\\X86_UOS'} for
file_name in file_list]
self.download_thread = DownloadThread(f'{self.TestWebApi}/TestProgramFileManager/download',
params_list, "")
self.download_thread.progress_updated.connect(self.progress_bar.setValue)
self.download_thread.speed_updated.connect(self.speed_label.setText)
self.download_thread.current_file.connect(self.current_file_label.setText) # 連接新信號
self.download_thread.start()
# 獲取文件列表和初始化下載線程的代碼保持不變
self.download_thread.download_completed.connect(self.on_download_completed)
self.download_thread.error_occurred.connect(self.on_error_occurred)
except Exception as e:
self.ShowLog(f"下載失敗: {e}",False)
#print(f"下載失敗: {e}")
#顯示當(dāng)前下載的文件
def update_current_file_label(self, file_name):
# 設(shè)置標(biāo)簽的文本
self.current_file_label.setText(f"Current File: {file_name}")
# 設(shè)置工具提示以顯示完整的文件名
self.current_file_label.setToolTip(file_name)
#窗體居中顯示
def centerWindow(self):
screen = QDesktopWidget().screenGeometry() # 獲取屏幕尺寸
# 設(shè)置窗口寬度為屏幕寬度的1/3,高度為屏幕高度的1/12
width = screen.width() * 2 / 3
height = screen.height() * 1 / 15
self.setFixedSize(width, height) # 設(shè)置固定大小
qr = self.frameGeometry() # 獲取主窗口的矩形框架
cp = screen.center() # 獲取顯示屏幕的中心點
qr.moveCenter(cp) # 將窗口框架的中心點移動到屏幕的中心位置
self.move(qr.topLeft()) # 將窗口的左上角移動到矩形框架的左上角,實際上就是將窗口居中
qr = self.frameGeometry() # 獲取主窗口的矩形框架
cp = QDesktopWidget().availableGeometry().center() # 獲取顯示屏幕的中心點
qr.moveCenter(cp) # 將窗口框架的中心點移動到屏幕的中心位置
self.move(qr.topLeft()) # 將窗口的左上角移動到矩形框架的左上角,實際上就是將窗口居中
def on_download_completed(self):
QApplication.exit(0) # 下載完成后正常退出
def on_error_occurred(self):
QApplication.exit(1) # 發(fā)生錯誤時退出并返回1
# 日志
def ShowLog(self, log, is_success):
try:
if is_success:
color = self.COLOR_SUCCESS
self.logger.info(log)
else:
color = self.COLOR_ERROR
self.logger.error(log)
# QMessageBox.critical(self, '系統(tǒng)提醒', f"{log}", QMessageBox.Yes, QMessageBox.Yes)
print(f"{color}{log}{self.COLOR_RESET}")
except Exception as e:
QMessageBox.critical(self, '系統(tǒng)提醒', f"{log}", QMessageBox.Yes, QMessageBox.Yes)
print(f"{self.COLOR_ERROR}{e}{self.COLOR_RESET}")
sys.exit(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_())
文章來源地址http://www.zghlxwxcb.cn/news/detail-847513.html
到了這里,關(guān)于WebApi+Python PyQ5實現(xiàn)大文件下載,Ui增加進度條和下載速率+已驗證uos和Windows環(huán)境的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!