甘肃省建设厅招标办网站/广告软文范例大全100字

甘肃省建设厅招标办网站,广告软文范例大全100字,什么软件可以发布广告信息,web开发框架效果: 大文件分片下载支持的功能: 展示目标文件信息提高下载速度:通过并发请求多个块,可以更有效地利用网络带宽断点续传:支持暂停后从已下载部分继续,无需重新开始错误恢复:单个块下载失败只…

效果:
在这里插入图片描述

大文件分片下载支持的功能:

  • 展示目标文件信息
  • 提高下载速度:通过并发请求多个块,可以更有效地利用网络带宽
  • 断点续传:支持暂停后从已下载部分继续,无需重新开始
  • 错误恢复:单个块下载失败只需重试该块,而不是整个文件
  • 更好的用户体验:实时显示下载进度、速度和预计剩余时间
  • 内存效率:通过分块下载和处理,减少了一次性内存占用

大文件分片下载

前端处理流程:

用户操作
重试次数<最大值
达到最大重试
取消当前请求
保存下载状态
暂停下载
重新开始未完成分块
恢复下载
取消所有请求
重置所有状态
取消下载
开始
获取文件信息
计算总分块数
初始化分块状态
并发下载多个分块
分块下载完成?
下载下一个分块
重试逻辑
错误处理
所有分块下载完成?
合并所有分块
创建完整文件
结束

后端处理流程:

后端
前端
API调用
API调用
返回文件元数据
file_info API
解析Range头
download_large_file API
定位文件指针
流式读取响应字节范围
返回206状态码和数据
获取文件信息
初始化下载器
计算分块策略
创建并发下载队列
发送Range请求
所有分块下载完成?
合并分块并下载

django代码

1,代码

# settings.py
# 指定文件访问的 URL 前缀
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media/'# views.py
import os
import mimetypes
from django.conf import settings
from django.http import StreamingHttpResponse, JsonResponse, HttpResponse
from django.utils.http import http_date
from django.views.decorators.http import require_http_methodsdef get_file_info(file_path):"""获取文件信息:- name: 文件名- size: 文件大小,单位字节- type: 文件类型"""if not os.path.exists(file_path):return Nonefile_size = os.path.getsize(file_path)file_name = os.path.basename(file_path)content_type, encoding = mimetypes.guess_type(file_path)return {'name': file_name,'size': file_size,'type': content_type or 'application/octet-stream'}@require_http_methods(["GET"])
def file_info(request):"""获取文件信息API"""file_path = os.path.join(settings.MEDIA_ROOT, "user_info_big.csv")info = get_file_info(file_path)if info is None:return JsonResponse({"error": "File not found"}, status=404)return JsonResponse(info)@require_http_methods(["GET"])
def download_large_file(request):"""分片下载文件的API:param request: 请求对象:return: 文件流"""file_path = os.path.join(settings.MEDIA_ROOT, "user_info_big.csv")# 1,检查文件是否存在if not os.path.exists(file_path):return HttpResponse("File not found", status=404)# 2,获取文件信息file_size = os.path.getsize(file_path)file_name = os.path.basename(file_path)content_type, encoding = mimetypes.guess_type(file_path)content_type = content_type or 'application/octet-stream'# 3,获取请求中的Range头range_header = request.META.get('HTTP_RANGE', '').strip()# 格式:bytes=0-100range_match = range_header.replace('bytes=', '').split('-')# 起始位置range_start = int(range_match[0]) if range_match[0] else 0# 结束位置range_end = int(range_match[1]) if range_match[1] else file_size - 1# 4,确保范围合法range_start = max(0, range_start)range_end = min(file_size - 1, range_end)# 5,计算实际要发送的数据大小content_length = range_end - range_start + 1# 6,创建响应:使用StreamingHttpResponse,将文件流式传输。206表示部分内容,200表示全部内容response = StreamingHttpResponse(file_iterator(file_path, range_start, range_end, chunk_size=8192),status=206 if range_header else 200,content_type=content_type)# 7,设置响应头response['Content-Length'] = content_lengthresponse['Accept-Ranges'] = 'bytes'response['Content-Disposition'] = f'attachment; filename="{file_name}"'if range_header:response['Content-Range'] = f'bytes {range_start}-{range_end}/{file_size}'response['Last-Modified'] = http_date(os.path.getmtime(file_path))# 模拟处理延迟,方便测试暂停/继续功能# time.sleep(0.1)  # 取消注释以添加人为延迟# 8,返回响应return responsedef file_iterator(file_path, start_byte=0, end_byte=None, chunk_size=8192):"""文件读取迭代器:param file_path: 文件路径:param start_byte: 起始字节:param end_byte: 结束字节:param chunk_size: 块大小"""with open(file_path, 'rb') as f:# 移动到起始位置f.seek(start_byte)# 计算剩余字节数remaining = end_byte - start_byte + 1 if end_byte else Nonewhile True:if remaining is not None:# 如果指定了结束位置,则读取剩余字节或块大小,取小的那个bytes_to_read = min(chunk_size, remaining)if bytes_to_read <= 0:breakelse:# 否则读取指定块大小bytes_to_read = chunk_sizedata = f.read(bytes_to_read)if not data:breakyield dataif remaining is not None:remaining -= len(data)# proj urls.py
from django.urls import path, includeurlpatterns = [# 下载文件path('download/', include(('download.urls', 'download'), namespace='download')),
]# download.urls.py
from django.urls import pathfrom download import viewsurlpatterns = [path('large_file/file_info/', views.file_info, name='file_info'),path('large_file/download_large_file/', views.download_large_file, name='download_large_file'),
]

2,核心功能解析

(1)file_info 端点 - 获取文件元数据

这个端点提供文件的基本信息,让前端能够规划下载策略:

  • 功能:返回文件名称、大小和MIME类型
  • 用途:前端根据文件大小和设置的块大小计算出需要下载的分块数量

(2)download_large_file 端点 - 实现分片下载

这是实现分片下载的核心API,通过HTTP Range请求实现:
1,解析Range头:从HTTP_RANGE头部解析客户端请求的字节范围

range_header = request.META.get('HTTP_RANGE', '').strip()
range_match = range_header.replace('bytes=', '').split('-')
range_start = int(range_match[0]) if range_match[0] else 0
range_end = int(range_match[1]) if range_match[1] else file_size - 1

2,流式传输:使用StreamingHttpResponse和迭代器按块读取和传输文件,避免一次加载整个文件到内存

response = StreamingHttpResponse(file_iterator(file_path, range_start, range_end, chunk_size=8192),status=206 if range_header else 200,content_type=content_type
)

3,返回响应头:设置必要的响应头,包括Content-Range指示返回内容的范围

response['Content-Range'] = f'bytes {range_start}-{range_end}/{file_size}'

(3)file_iterator 函数 - 高效的文件读取

这个函数创建一个迭代器,高效地读取文件的指定部分:

1,文件定位:将文件指针移动到请求的起始位置

f.seek(start_byte)

2,分块读取:按指定的块大小读取文件,避免一次性读取大量数据

data = f.read(bytes_to_read)

3,边界控制:确保只读取请求范围内的数据

remaining -= len(data)

HTTP状态码和响应头的作用

1,206 Partial Content:

  • 表示服务器成功处理了部分GET请求
  • 分片下载的标准HTTP状态码

2,Content-Range: bytes start-end/total:

  • 指示响应中包含的字节范围和文件总大小
  • 帮助客户端确认接收的是哪部分数据

3,Accept-Ranges: bytes:

  • 表明服务器支持范围请求
  • 让客户端知道可以使用Range头请求部分内容

4,Content-Length:

  • 表示当前响应内容的长度
  • 不是文件总长度,而是本次返回的片段长度

vue3代码

1,代码

1,前端界面 (Vue组件):

  • 提供配置选项:并发块数、块大小
  • 显示下载进度:进度条、已下载量、下载速度、剩余时间提供操作按钮:开始、暂停、继续、取消
  • 可视化显示每个分块的下载状态
<template><div class="enhanced-downloader"><div class="card"><h2>大文件分块下载</h2><div class="file-info" v-if="fileInfo"><p><strong>文件名:</strong> {{ fileInfo.name }}</p><p><strong>文件大小:</strong> {{ formatFileSize(fileInfo.size) }}</p><p><strong>类型:</strong> {{ fileInfo.type }}</p></div><div class="config-panel" v-if="!isDownloading && !isPaused"><div class="config-item"><label>并发块数:</label><select v-model="concurrency"><option :value="1">1</option><option :value="2">2</option><option :value="3">3</option><option :value="5">5</option><option :value="8">8</option></select></div><div class="config-item"><label>块大小:</label><select v-model="chunkSize"><option :value="512 * 1024">512 KB</option><option :value="1024 * 1024">1 MB</option><option :value="2 * 1024 * 1024">2 MB</option><option :value="5 * 1024 * 1024">5 MB</option></select></div></div><div class="progress-container" v-if="isDownloading || isPaused"><div class="progress-bar"><div class="progress" :style="{ width: `${progress}%` }"></div></div><div class="progress-stats"><div class="stat-item"><span class="label">进度:</span><span class="value">{{ progress.toFixed(2) }}%</span></div><div class="stat-item"><span class="label">已下载:</span><span class="value">{{ formatFileSize(downloadedBytes) }} / {{ formatFileSize(totalBytes) }}</span></div><div class="stat-item"><span class="label">速度:</span><span class="value">{{ downloadSpeed }}</span></div><div class="stat-item"><span class="label">已完成块:</span><span class="value">{{ downloadedChunks }} / {{ totalChunks }}</span></div><div class="stat-item"><span class="label">剩余时间:</span><span class="value">{{ remainingTime }}</span></div></div></div><div class="chunk-visualization" v-if="isDownloading || isPaused"><div class="chunk-grid"><divv-for="(chunk, index) in chunkStatus":key="index"class="chunk-block":class="{'downloaded': chunk === 'completed','downloading': chunk === 'downloading','pending': chunk === 'pending','error': chunk === 'error'}":title="`块 ${index + 1}: ${chunk}`"></div></div></div><div class="actions"><button@click="startDownload":disabled="isDownloading"v-if="!isDownloading && !isPaused"class="btn btn-primary">开始下载</button><button@click="pauseDownload":disabled="!isDownloading"v-if="isDownloading && !isPaused"class="btn btn-warning">暂停</button><button@click="resumeDownload":disabled="!isPaused"v-if="isPaused"class="btn btn-success">继续</button><button@click="cancelDownload":disabled="!isDownloading && !isPaused"class="btn btn-danger">取消</button></div><div class="status-message" v-if="statusMessage">{{ statusMessage }}</div></div></div>
</template><script setup>
import {computed, onMounted, onUnmounted, ref, watch} from 'vue';
import {ChunkDownloader} from './downloadService';// API URL
const API_BASE_URL = 'http://localhost:8000/download/';// 下载配置
const concurrency = ref(3);
const chunkSize = ref(1024 * 1024); // 1MB
const downloader = ref(null);// 状态变量
const fileInfo = ref(null);
const isDownloading = ref(false);
const isPaused = ref(false);
const downloadedBytes = ref(0);
const totalBytes = ref(0);
const downloadedChunks = ref(0);
const totalChunks = ref(0);
const statusMessage = ref('准备就绪');
const downloadStartTime = ref(0);
const lastUpdateTime = ref(0);
const lastBytes = ref(0);
const downloadSpeed = ref('0 KB/s');
const remainingTime = ref('计算中...');
const speedInterval = ref(null);// 块状态
const chunkStatus = ref([]);// 计算下载进度百分比
const progress = computed(() => {if (totalBytes.value === 0) return 0;return (downloadedBytes.value / totalBytes.value) * 100;
});// 初始化
onMounted(async () => {try {await fetchFileInfo();} catch (error) {console.error('获取文件信息失败:', error);statusMessage.value = `获取文件信息失败: ${error.message}`;}
});// 清理资源
onUnmounted(() => {if (downloader.value) {downloader.value.cancel();}clearInterval(speedInterval.value);
});// 获取文件信息
async function fetchFileInfo() {const response = await fetch(`${API_BASE_URL}large_file/file_info/`);if (!response.ok) {throw new Error(`HTTP 错误! 状态码: ${response.status}`);}fileInfo.value = await response.json();totalBytes.value = fileInfo.value.size;// 根据文件大小初始化分块状态const initialTotalChunks = Math.ceil(fileInfo.value.size / chunkSize.value);chunkStatus.value = Array(initialTotalChunks).fill('pending');totalChunks.value = initialTotalChunks;
}// 开始下载
async function startDownload() {if (isDownloading.value) return;try {// 初始化下载器downloader.value = new ChunkDownloader(`${API_BASE_URL}`, {chunkSize: chunkSize.value,concurrency: concurrency.value,maxRetries: 3,onProgress: handleProgress,onComplete: handleComplete,onError: handleError,onStatusChange: handleStatusChange});// 初始化状态downloadedBytes.value = 0;downloadedChunks.value = 0;isDownloading.value = true;isPaused.value = false;statusMessage.value = '准备下载...';// 获取文件信息await downloader.value.fetchFileInfo();// 更新总块数totalChunks.value = Math.ceil(downloader.value.fileSize / chunkSize.value);chunkStatus.value = Array(totalChunks.value).fill('pending');// 开始下载downloadStartTime.value = Date.now();lastUpdateTime.value = Date.now();lastBytes.value = 0;startSpeedCalculator();await downloader.value.start();} catch (error) {console.error('下载启动失败:', error);statusMessage.value = `下载启动失败: ${error.message}`;isDownloading.value = false;}
}// 暂停下载
function pauseDownload() {if (!isDownloading.value || !downloader.value) return;downloader.value.pause();isDownloading.value = false;isPaused.value = true;statusMessage.value = '下载已暂停';clearInterval(speedInterval.value);
}// 继续下载
function resumeDownload() {if (!isPaused.value || !downloader.value) return;downloader.value.resume();isDownloading.value = true;isPaused.value = false;statusMessage.value = '继续下载...';// 重新开始速度计算lastUpdateTime.value = Date.now();lastBytes.value = downloadedBytes.value;startSpeedCalculator();
}// 取消下载
function cancelDownload() {if (!downloader.value) return;downloader.value.cancel();isDownloading.value = false;isPaused.value = false;downloadedBytes.value = 0;downloadedChunks.value = 0;statusMessage.value = '下载已取消';clearInterval(speedInterval.value);// 重置块状态chunkStatus.value = Array(totalChunks.value).fill('pending');
}// 处理进度更新
function handleProgress(data) {downloadedBytes.value = data.downloadedBytes;downloadedChunks.value = data.downloadedChunks;// 更新块状态(这里仅是简化的更新方式,实际上应该由downloader提供精确的块状态)const newChunkStatus = [...chunkStatus.value];const completedChunksCount = Math.floor(downloadedChunks.value);for (let i = 0; i < newChunkStatus.length; i++) {if (i < completedChunksCount) {newChunkStatus[i] = 'completed';} else if (i < completedChunksCount + concurrency.value && newChunkStatus[i] !== 'completed') {newChunkStatus[i] = 'downloading';}}chunkStatus.value = newChunkStatus;
}// 处理下载完成
function handleComplete(data) {isDownloading.value = false;isPaused.value = false;statusMessage.value = '下载完成';clearInterval(speedInterval.value);// 标记所有块为已完成chunkStatus.value = Array(totalChunks.value).fill('completed');
}// 处理错误
function handleError(error) {console.error('下载错误:', error);statusMessage.value = `下载错误: ${error.message}`;
}// 处理状态变化
function handleStatusChange(status, error) {switch (status) {case 'downloading':isDownloading.value = true;isPaused.value = false;statusMessage.value = '下载中...';break;case 'paused':isDownloading.value = false;isPaused.value = true;statusMessage.value = '下载已暂停';break;case 'completed':isDownloading.value = false;isPaused.value = false;statusMessage.value = '下载完成';break;case 'error':isDownloading.value = false;statusMessage.value = `下载错误: ${error?.message || '未知错误'}`;break;}
}// 启动下载速度计算器
function startSpeedCalculator() {clearInterval(speedInterval.value);speedInterval.value = setInterval(() => {const now = Date.now();const timeElapsed = (now - lastUpdateTime.value) / 1000; // 转换为秒const bytesDownloaded = downloadedBytes.value - lastBytes.value;if (timeElapsed > 0) {const speed = bytesDownloaded / timeElapsed; // 字节/秒downloadSpeed.value = formatFileSize(speed) + '/s';// 计算剩余时间if (speed > 0) {const bytesRemaining = totalBytes.value - downloadedBytes.value;const secondsRemaining = bytesRemaining / speed;remainingTime.value = formatTime(secondsRemaining);} else {remainingTime.value = '计算中...';}lastUpdateTime.value = now;lastBytes.value = downloadedBytes.value;}}, 1000);
}// 格式化文件大小
function formatFileSize(bytes) {if (bytes === 0) return '0 B';const k = 1024;const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}// 格式化时间
function formatTime(seconds) {if (!isFinite(seconds) || seconds < 0) {return '计算中...';}if (seconds < 60) {return `${Math.ceil(seconds)}`;} else if (seconds < 3600) {const minutes = Math.floor(seconds / 60);const secs = Math.ceil(seconds % 60);return `${minutes}${secs}`;} else {const hours = Math.floor(seconds / 3600);const minutes = Math.floor((seconds % 3600) / 60);return `${hours}小时${minutes}分钟`;}
}// 监听配置改变,更新块状态
watch([chunkSize], () => {if (fileInfo.value && fileInfo.value.size) {totalChunks.value = Math.ceil(fileInfo.value.size / chunkSize.value);chunkStatus.value = Array(totalChunks.value).fill('pending');}
});
</script><style scoped>
.enhanced-downloader {max-width: 800px;margin: 0 auto;padding: 20px;
}.card {background: #fff;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);padding: 20px;
}h2 {margin-top: 0;color: #333;
}.file-info {margin-bottom: 20px;
}.progress-container {margin-bottom: 20px;
}.progress-bar {height: 20px;background-color: #f0f0f0;border-radius: 10px;overflow: hidden;margin-bottom: 10px;
}.progress {height: 100%;background-color: #4CAF50;transition: width 0.3s ease;
}.progress-stats {display: flex;justify-content: space-between;font-size: 14px;color: #666;
}.actions {display: flex;gap: 10px;margin-bottom: 20px;
}.btn {padding: 8px 16px;border: none;border-radius: 4px;cursor: pointer;font-weight: bold;transition: background-color 0.3s;
}.btn:disabled {opacity: 0.5;cursor: not-allowed;
}.btn-primary {background-color: #4CAF50;color: white;
}.btn-warning {background-color: #FF9800;color: white;
}.btn-success {background-color: #2196F3;color: white;
}.btn-danger {background-color: #F44336;color: white;
}.status {font-style: italic;color: #666;
}
</style>

2,下载服务 (ChunkDownloader类):

  • 负责管理整个下载过程
  • 处理文件信息获取、分块下载、进度追踪
  • 实现并发控制、重试机制、暂停/继续功能
// downloadService.js - 分块下载实现/*文件分块下载器*/
export class ChunkDownloader {constructor(url, options = {}) {this.url = url;this.chunkSize = options.chunkSize || 1024 * 1024; // 默认1MB每块this.maxRetries = options.maxRetries || 3;this.concurrency = options.concurrency || 3; // 并发下载块数this.timeout = options.timeout || 30000; // 超时时间this.fileSize = 0;this.fileName = '';this.contentType = '';this.chunks = [];this.downloadedChunks = 0;this.activeDownloads = 0;this.totalChunks = 0;this.downloadedBytes = 0;this.status = 'idle'; // idle, downloading, paused, completed, errorthis.error = null;this.onProgress = options.onProgress || (() => {});this.onComplete = options.onComplete || (() => {});this.onError = options.onError || (() => {});this.onStatusChange = options.onStatusChange || (() => {});this.abortControllers = new Map();this.pendingChunks = [];this.processedChunks = new Set();}// 获取文件信息async fetchFileInfo() {try {const response = await fetch(this.url + 'large_file/file_info/');if (!response.ok) {throw new Error(`无法获取文件信息: ${response.status}`);}const info = await response.json();this.fileSize = info.size;this.fileName = info.name;this.contentType = info.type;// 计算分块数量this.totalChunks = Math.ceil(this.fileSize / this.chunkSize);return info;} catch (error) {this.error = error;this.status = 'error';this.onStatusChange(this.status, error);this.onError(error);throw error;}}// 开始下载async start() {if (this.status === 'downloading') {return;}try {// 如果还没获取文件信息,先获取if (this.fileSize === 0) {await this.fetchFileInfo();}// 初始化状态this.status = 'downloading';this.onStatusChange(this.status);// 如果是全新下载,初始化块数组if (this.chunks.length === 0) {this.chunks = new Array(this.totalChunks).fill(null);this.pendingChunks = Array.from({length: this.totalChunks}, (_, i) => i);}// 开始并发下载this.startConcurrentDownloads();} catch (error) {this.error = error;this.status = 'error';this.onStatusChange(this.status, error);this.onError(error);}}// 开始并发下载startConcurrentDownloads() {// 确保同时只有指定数量的并发下载while (this.activeDownloads < this.concurrency && this.pendingChunks.length > 0) {const chunkIndex = this.pendingChunks.shift();this.downloadChunk(chunkIndex);}}// 下载指定的块async downloadChunk(chunkIndex, retryCount = 0) {if (this.status !== 'downloading' || this.processedChunks.has(chunkIndex)) {return;}this.activeDownloads++;const startByte = chunkIndex * this.chunkSize;const endByte = Math.min(startByte + this.chunkSize - 1, this.fileSize - 1);// 创建用于取消请求的控制器const controller = new AbortController();this.abortControllers.set(chunkIndex, controller);try {const response = await fetch(this.url + 'large_file/download_large_file/',{method: 'GET',headers: {'Range': `bytes=${startByte}-${endByte}`},signal: controller.signal,timeout: this.timeout});if (!response.ok && response.status !== 206) {throw new Error(`服务器错误: ${response.status}`);}// 获取块数据const blob = await response.blob();this.chunks[chunkIndex] = blob;this.downloadedChunks++;this.downloadedBytes += blob.size;this.processedChunks.add(chunkIndex);// 更新进度this.onProgress({downloadedChunks: this.downloadedChunks,totalChunks: this.totalChunks,downloadedBytes: this.downloadedBytes,totalBytes: this.fileSize,progress: (this.downloadedBytes / this.fileSize) * 100});// 清理控制器this.abortControllers.delete(chunkIndex);// 检查是否下载完成if (this.downloadedChunks === this.totalChunks) {this.completeDownload();} else if (this.status === 'downloading') {// 继续下载下一个块this.activeDownloads--;this.startConcurrentDownloads();}} catch (error) {this.abortControllers.delete(chunkIndex);if (error.name === 'AbortError') {// 用户取消,不进行重试this.activeDownloads--;return;}// 重试逻辑if (retryCount < this.maxRetries) {console.warn(`${chunkIndex} 下载失败,重试 ${retryCount + 1}/${this.maxRetries}`);this.activeDownloads--;this.downloadChunk(chunkIndex, retryCount + 1);} else {console.error(`${chunkIndex} 下载失败,已达到最大重试次数`);this.error = error;this.status = 'error';this.onStatusChange(this.status, error);this.onError(error);this.activeDownloads--;}}}// 完成下载completeDownload() {if (this.status === 'completed') {return;}try {// 合并所有块const blob = new Blob(this.chunks, {type: this.contentType});// 创建下载链接const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = this.fileName;document.body.appendChild(a);a.click();document.body.removeChild(a);// 清理资源setTimeout(() => URL.revokeObjectURL(url), 100);// 更新状态this.status = 'completed';this.onStatusChange(this.status);this.onComplete({fileName: this.fileName,fileSize: this.fileSize,contentType: this.contentType,blob: blob});} catch (error) {this.error = error;this.status = 'error';this.onStatusChange(this.status, error);this.onError(error);}}// 暂停下载pause() {if (this.status !== 'downloading') {return;}// 取消所有正在进行的请求this.abortControllers.forEach(controller => {controller.abort();});// 清空控制器集合this.abortControllers.clear();// 更新状态this.status = 'paused';this.activeDownloads = 0;this.onStatusChange(this.status);// 将当前处理中的块重新加入待处理队列this.pendingChunks = Array.from({length: this.totalChunks}, (_, i) => i).filter(i => !this.processedChunks.has(i));}// 继续下载resume() {if (this.status !== 'paused') {return;}this.status = 'downloading';this.onStatusChange(this.status);this.startConcurrentDownloads();}// 取消下载cancel() {// 取消所有正在进行的请求this.abortControllers.forEach(controller => {controller.abort();});// 重置所有状态this.chunks = [];this.downloadedChunks = 0;this.activeDownloads = 0;this.downloadedBytes = 0;this.status = 'idle';this.error = null;this.abortControllers.clear();this.pendingChunks = [];this.processedChunks.clear();this.onStatusChange(this.status);}// 获取当前状态getStatus() {return {status: this.status,downloadedChunks: this.downloadedChunks,totalChunks: this.totalChunks,downloadedBytes: this.downloadedBytes,totalBytes: this.fileSize,progress: this.fileSize ? (this.downloadedBytes / this.fileSize) * 100 : 0,fileName: this.fileName,error: this.error};}
}

2,核心技术原理

(1)HTTP Range请求

该实现通过HTTP的Range头部实现分块下载:

const response = await fetch(this.url + 'large_file/download_large_file/',{method: 'GET',headers: {'Range': `bytes=${startByte}-${endByte}`},signal: controller.signal,timeout: this.timeout});
  • 服务器会返回状态码206(Partial Content)和请求的文件片段。

(2)并发控制

代码通过控制同时活跃的下载请求数量来实现并发:

while (this.activeDownloads < this.concurrency && this.pendingChunks.length > 0) {const chunkIndex = this.pendingChunks.shift();this.downloadChunk(chunkIndex);
}

(3)状态管理和进度追踪

  • 跟踪每个块的下载状态(待下载、下载中、已完成、错误)
  • 计算并报告总体进度、下载速度和剩余时间

(4)错误处理和重试机制

对下载失败的块进行自动重试:

if (retryCount < this.maxRetries) {console.warn(`${chunkIndex} 下载失败,重试 ${retryCount + 1}/${this.maxRetries}`);this.activeDownloads--;this.downloadChunk(chunkIndex, retryCount + 1);
}

(5)暂停/恢复功能

通过AbortController取消活跃的请求,并保存未完成的块索引:

pause() {// 取消所有正在进行的请求this.abortControllers.forEach(controller => {controller.abort();});// 将当前处理中的块重新加入待处理队列this.pendingChunks = Array.from({length: this.totalChunks}, (_, i) => i).filter(i => !this.processedChunks.has(i));
}

(6)文件合并和下载

所有块下载完成后,使用Blob API合并所有分块并创建下载链接:

const blob = new Blob(this.chunks, {type: this.contentType});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = this.fileName;
a.click();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/72539.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ICMP、UDP以及IP、ARP报文包的仲裁处理

在之前的章节中&#xff0c;笔者就UDP、ICMP、IP、ARP、MAC层以及巨型帧等做了详细介绍以及代码实现及仿真&#xff0c;从本章节开始&#xff0c;笔者将就各个模块组合在一起&#xff0c;实现UDP协议栈的整体收发&#xff0c;在实现模块的整体组合之前&#xff0c;还需要考虑一…

【NLP 38、实践 ⑩ NER 命名实体识别任务 Bert 实现】

去做具体的事&#xff0c;然后稳稳托举自己 —— 25.3.17 数据文件&#xff1a; 通过网盘分享的文件&#xff1a;Ner命名实体识别任务 链接: https://pan.baidu.com/s/1fUiin2um4PCS5i91V9dJFA?pwdyc6u 提取码: yc6u --来自百度网盘超级会员v3的分享 一、配置文件 config.py …

Linux 蓝牙音频软件栈实现分析

Linux 蓝牙音频软件栈实现分析 蓝牙协议栈简介蓝牙控制器探测BlueZ 插件系统及音频插件蓝牙协议栈简介 蓝牙协议栈是实现蓝牙通信功能的软件架构,它由多个层次组成,每一层负责特定的功能。蓝牙协议栈的设计遵循蓝牙标准 (由蓝牙技术联盟,Bluetooth SIG 定义),支持多种蓝牙…

JetBrains(全家桶: IDEA、WebStorm、GoLand、PyCharm) 2024.3+ 2025 版免费体验方案

JetBrains&#xff08;全家桶: IDEA、WebStorm、GoLand、PyCharm&#xff09; 2024.3 2025 版免费体验方案 前言 JetBrains IDE 是许多开发者的主力工具&#xff0c;但从 2024.02 版本起&#xff0c;JetBrains 调整了试用政策&#xff0c;新用户不再享有默认的 30 天免费试用…

Prosys OPC UA Gateway:实现 OPC Classic 与 OPC UA 无缝连接

在工业自动化的数字化转型中&#xff0c;设备与系统之间的高效通信至关重要。然而&#xff0c;许多企业仍依赖于基于 COM/DCOM 技术的 OPC 产品&#xff0c;这给与现代化的 OPC UA 架构的集成带来了挑战。 Prosys OPC UA Gateway 正是为解决这一问题而生&#xff0c;它作为一款…

基于银河麒麟系统ARM架构安装达梦数据库并配置主从模式

达梦数据库简要概述 达梦数据库&#xff08;DM Database&#xff09;是一款由武汉达梦公司开发的关系型数据库管理系统&#xff0c;支持多种高可用性和数据同步方案。在主从模式&#xff08;也称为 Master-Slave 或 Primary-Secondary 模式&#xff09;中&#xff0c;主要通过…

系统思考全球化落地

感谢加密货币公司Bybit的再次邀请&#xff0c;为全球团队分享系统思考课程&#xff01;虽然大家来自不同国家&#xff0c;线上学习的形式依然让大家充满热情与互动&#xff0c;思维的碰撞不断激发新的灵感。 尽管时间存在挑战&#xff0c;但我看到大家的讨论异常积极&#xff…

Figma的汉化

Figma的汉化插件有客户端版本与Chrome版本&#xff0c;大家可根据自己的需要进行选择。 下载插件 进入Figma软件汉化-Figma中文版下载-Figma中文社区使用客户端&#xff1a;直接下载客户端使用网页版&#xff1a;安装chrome浏览器汉化插件国外推荐前往chrome商店安装国内推荐下…

IDEA 一键完成:打包 + 推送 + 部署docker镜像

1、本方案要解决场景&#xff1f; 想直接通过本地 IDEA 将最新的代码部署到远程服务器上。 2、本方案适用于什么样的项目&#xff1f; 项目是一个 Spring Boot 的 Java 项目。项目用 maven 进行管理。项目的运行基于 docker 容器&#xff08;即项目将被打成 docker image&am…

SpringBoot 第一课(Ⅲ) 配置类注解

目录 一、PropertySource 二、ImportResource ①SpringConfig &#xff08;Spring框架全注解&#xff09; ②ImportResource注解实现 三、Bean 四、多配置文件 多Profile文件的使用 文件命名约定&#xff1a; 激活Profile&#xff1a; YAML文件支持多文档块&#xff…

深度解析React Native底层核心架构

React Native 工作原理深度解析 一、核心架构&#xff1a;三层异构协作体系 React Native 的跨平台能力源于其独特的 JS层-Shadow层-Native层 架构设计&#xff0c;三者在不同线程中协同工作&#xff1a; JS层 运行于JavaScriptCore&#xff08;iOS&#xff09;或Hermes&…

对话智能体的正确打开方式:解析主流AI聊天工具的核心能力与使用方式

一、人机对话的黄金法则 在与人工智能对话系统交互时&#xff0c;掌握以下七项核心原则可显著提升沟通效率&#xff1a;文末有教程分享地址 意图精准表达术 采用"背景需求限定条件"的结构化表达 示例优化&#xff1a;"请用Python编写一个网络爬虫&#xff08…

Xinference大模型配置介绍并通过git-lfs、hf-mirror安装

文章目录 一、Xinference开机服务systemd二、语言&#xff08;LLM&#xff09;模型2.1 配置介绍2.2 DeepSeek-R1-Distill-Qwen-32B&#xff08;大杯&#xff09;工具下载git-lfs&#xff08;可以绕过Hugging Face&#xff09; 2.3 DeepSeek-R1-Distill-Qwen-32B-Q4_K_M-GGUF&am…

MyBatis操纵数据库-XML实现(补充)

目录 一.多表查询二.MyBatis参数赋值(#{ }和${ })2.1 #{ }和${ }的使用2.2 #{ }和${ }的区别2.3 SQL注入2.3 ${ }的应用场景2.3.1 排序功能2.3.2 like查询 一.多表查询 多表查询的操作和单表查询基本相同&#xff0c;只需改变一下SQL语句&#xff0c;同时也要在实体类中创建出…

快速导出接口设计表——基于DOMParser的Swagger接口详情半自动化提取方法

作者声明&#xff1a;不想看作者声明的&#xff08;需要生成接口设计表的&#xff09;直接前往https://capujin.github.io/A2T/。 注&#xff1a;Github Pages生成的页面可能会出现访问不稳定&#xff0c;暂时没将源码上传至Github&#xff0c;如有需要&#xff0c;可联系我私…

AI-医学影像分割方法与流程

AI医学影像分割方法与流程–基于低场磁共振影像的病灶识别 – 作者:coder_fang AI框架&#xff1a;PaddleSeg 数据准备&#xff0c;使用MedicalLabelMe进行dcm文件标注&#xff0c;产生同名.json文件。 编写程序生成训练集图片&#xff0c;包括掩码图。 代码如下: def doC…

SGMEA: Structure-Guided Multimodal Entity Alignment

3 Method 3.1 Problem Definition 3.2 Framework Description 总体框架如图2所示&#xff0c;由三个主要部分组成&#xff1a;初始嵌入采集模块、结构引导模块和模态融合模块。 3.3 Initial Embedding Acquisition 3.3.1 Structural Embedding 3.3.2 Relation, Attribute, …

《基于超高频RFID的图书馆管理系统的设计与实现》开题报告

一、研究背景与意义 1.研究背景 随着信息化时代的到来&#xff0c;运用计算机科学技术实现图书馆的管理工作已成为优势。更加科学地管理图书馆会大大提高工作效率。我国的图书管理体系发展经历了三个阶段&#xff1a;传统图书管理模式、现代图书管理模式以及基于无线射频识别&…

[local-file-system]基于服务器磁盘的本地文件存储方案

[local-file-system]基于服务器磁盘的本地文件存储方案 仅提供后端方案 github 环境 JDK11linux/windows/mac 应用场景 适用于ToB业务&#xff0c;中小企业的单体服务&#xff0c;仅使用磁盘存储文件的解决方案 仅使用服务器磁盘存储 与业务实体相结合的文件存储方案&…

【蓝桥杯每日一题】3.16

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 目录 3.9 高精度算法 一、高精度加法 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a; 解题代码&#xff1a; 二、高精度减法 题目链接&#xff1a; 题目描述&…