2026/9/16 14:20:49

HuLa 七牛云上传功能实战指南:统一上传接口、分片上传与 Rust 端断点续传原理

HuLa 七牛云上传功能实战指南:统一上传接口、分片上传与 Rust 端断点续传原理 HuLa 七牛云上传功能实战指南统一上传接口、分片上传与 Rust 端断点续传原理【免费下载链接】HuLa A cross-platform instant messaging desktop application with exceptional performance built on Rust Vue3, compatible with Windows, macOS, Linux, Android, and iOS一款基于RustVue3极致性能的跨平台即时通讯桌面应用兼容Windows、MacOS、Linux、Android、IOS项目地址: https://gitcode.com/GitHub_Trending/hu/HuLaHuLa 是基于 Rust Vue3 构建的跨平台即时通讯应用其在默认上传方式之外集成了七牛云Qiniu Cloud对象存储为文件、图片、头像等资源提供了更灵活的存储选择。本文以 docs/qiniu-upload.md 为核心骨架结合 src/hooks/useUpload.ts、src-tauri/src/command/upload_command.rs 等源码实现完整讲解区域配置、统一上传接口、分片上传 API 调用细节与消息策略集成方式。读完本文你将掌握如何在 HuLa 中接入七牛云、切换上传 Provider、启用分片上传并理解前端 TypeScript 与后端 Rust 命令之间的完整调用链路。功能特点HuLa 的七牛云上传能力围绕以下特性设计上传方式可切换可在默认上传方式、七牛云、MinIO 之间灵活切换前端通过UploadProviderEnum统一标识统一上传接口useUploadHook 提供getUploadAndDownloadUrl与doUpload两个核心方法业务方无需关心底层存储差异支持大文件上传内置 4MB 分片阈值超过阈值自动切换为分片上传自动生成唯一文件名默认以场景/时间戳_文件名命名可选启用基于文件 MD5 哈希的去重命名避免文件名冲突支持进度显示在浏览器与 Tauri 环境中均通过Channel回传上传进度支持多区域上传华东、华北、华南、北美、东南亚五个区域未指定时默认华南z2支持分片上传大文件基于七牛云 mkblk / bput / mkfile 分块上传 API由 Rust 端实现qiniu_upload_resumable天然支持断点续传与精确进度反馈整体架构从前端 Hook 到 Rust 命令的调用链路从源码结构看七牛云上传从前端到存储侧共分四层调用链路如下业务层消息策略、聊天窗口、头像上传等调用useUpload()返回的 Hook 方法凭证层src/utils/ImRequestUtils.ts 中的getQiniuToken()通过imRequest请求后端GET_QINIU_TOKEN对应ImUrlEnum.GET_QINIU_TOKEN getQiniuToken换取上传 Token 与域名配置getUploadProvider()请求STORAGE_PROVIDER获取默认存储提供者qiniu / minio前端执行层src/hooks/useUpload.ts 负责组装 FormData、选择普通上传或分片上传Rust 命令层src-tauri/src/command/upload_command.rs 提供upload_file_putPUT 流式上传与qiniu_upload_resumable七牛分片上传两个 Tauri 命令二者均在 src-tauri/src/lib.rs 注册。凭证获取入口定义在 src/enums/index.ts/** 获取七牛云token */ GET_QINIU_TOKEN getQiniuToken, /** 初始化配置 */ INIT_CONFIG initConfig, /** 获取默认存储提供者 */ STORAGE_PROVIDER storageProvider,其中GET_QINIU_TOKEN与STORAGE_PROVIDER是切换上传方式的两个关键后端接口前者按场景返回上传凭证后者决定未显式指定 Provider 时的默认存储。配置说明七牛云区域设置七牛云提供多个存储区域HuLa 根据配置自动选择对应的上传域名区域代码区域名称上传域名z0华东https://upload.qiniup.comz1华北https://upload-z1.qiniup.comz2华南https://upload-z2.qiniup.comna0北美https://upload-na0.qiniup.comas0东南亚https://upload-as0.qiniup.com默认情况下如果未指定区域系统将使用华南区域z2。region属于可选字段仅在需要覆盖默认区域时由后端下发。后端配置后端需为 HuLa 提供以下配置信息{ token: 七牛云上传凭证, domain: 七牛云存储域名, storagePrefix: 存储前缀, region: 存储区域代码可选默认为z2 }其中domain既可用于直传也是生成下载地址的基础storagePrefix在分片上传场景下会作为对象 Key 的顶层目录。此外客户端侧还依赖一份全局配置。在 src/services/types.ts 中定义了ConfigType.qiNiu结构/** 七牛 */ qiNiu: { /** oss域名 */ ossDomain: string /** 分片大小 */ fragmentSize: string /** 超过多少MB开启分片上传 */ turnSharSize: string }ossDomain用于拼接最终下载地址如uploadToQiniu中的${configStore.config.qiNiu.ossDomain}/${result.key || key}fragmentSize与turnSharSize则对应服务端下发的分片大小与分片开启阈值。前端在 src/stores/config.ts 中通过getQiNiuConfig()读取该配置。上传 Provider 枚举与上传选项src/hooks/useUpload.ts 中定义了上传方式与选项/** 上传方式 */ export enum UploadProviderEnum { /** 默认上传方式 */ DEFAULT default, /** 七牛云上传 */ QINIU qiniu, /** MinIO 上传 */ MINIO minio } /** 上传配置 */ export interface UploadOptions { /** 上传方式 */ provider?: UploadProviderEnum /** 上传场景 */ scene?: UploadSceneEnum /** 是否使用分片上传仅对七牛云有效 */ useChunks?: boolean /** 分片大小单位字节默认4MB */ chunkSize?: number /** 是否启用文件去重使用文件哈希作为文件名 */ enableDeduplication?: boolean }上传场景枚举定义在 src/enums/index.ts/** 上传scene值状态 */ export enum UploadSceneEnum { /** 聊天 */ CHAT chat, /** 表情 */ EMOJI emoji, /** 头像 */ AVATAR avatar }场景值会作为对象 Key 的目录前缀例如chat/1688888888888_photo.jpg也可用于后端按场景签发不同权限的 Token。使用方法在代码中使用核心用法是「先取凭证、后执行上传」两步式调用。通过getUploadAndDownloadUrl获取上传与下载地址再交给doUpload执行import { useUpload, UploadProviderEnum } from /hooks/useUpload import { UploadSceneEnum } from /enums // 创建上传hook实例 const uploadHook useUpload() // 使用七牛云上传 async function uploadToQiniu(filePath: string) { try { // 获取上传和下载URL const result await uploadHook.getUploadAndDownloadUrl(filePath, { provider: UploadProviderEnum.QINIU, scene: UploadSceneEnum.CHAT }) // 执行上传 await uploadHook.doUpload(filePath, result.uploadUrl, result) console.log(上传成功下载地址:, result.downloadUrl) return result.downloadUrl } catch (error) { console.error(上传失败:, error) throw error } } // 使用默认上传方式 async function uploadWithDefault(filePath: string) { try { // 获取上传和下载URL const result await uploadHook.getUploadAndDownloadUrl(filePath, { provider: UploadProviderEnum.DEFAULT, scene: UploadSceneEnum.CHAT }) // 执行上传 await uploadHook.doUpload(filePath, result.uploadUrl, { downloadUrl: result.downloadUrl }) console.log(上传成功下载地址:, result.downloadUrl) return result.downloadUrl } catch (error) { console.error(上传失败:, error) throw error } }从源码看getUploadAndDownloadUrlsrc/hooks/useUpload.ts内部逻辑为优先使用显式传入的provider若未传入则调用getUploadProvider()读取后端默认 Provider。对于QINIU分支它通过getQiniuToken({ scene, fileName })换取凭证若响应含token则说明后端下发了七牛配置uploadUrl被标记为UploadProviderEnum.QINIUdownloadUrl即存储域名若响应含uploadUrl则实际返回的是 MinIO 预签名地址。doUploadsrc/hooks/useUpload.ts则负责真正上传当uploadUrl UploadProviderEnum.QINIU且凭证缺失时会自动重新获取七牛 Token并回填domain、token、storagePrefix、region文件大小超过 500MB 上限MAX_FILE_SIZE会直接报错上传通过TauriCommand.QINIU_UPLOAD_RESUMABLE命令在 Rust 端完成成功后返回${configStore.config.qiNiu.ossDomain}/${key}作为下载地址普通上传方式则走TauriCommand.UPLOAD_FILE_PUTPUT 请求返回值是options.downloadUrl。使用分片上传对于大文件可显式指定useChunks与chunkSize使用七牛云分片上传import { useUpload, UploadProviderEnum } from /hooks/useUpload import { UploadSceneEnum } from /enums // 创建上传hook实例 const uploadHook useUpload() // 使用七牛云分片上传 async function uploadLargeFileToQiniu(filePath: string) { try { // 获取上传和下载URL const result await uploadHook.getUploadAndDownloadUrl(filePath, { provider: UploadProviderEnum.QINIU, scene: UploadSceneEnum.CHAT }) // 执行分片上传指定使用分片上传和分片大小可选默认4MB await uploadHook.doUpload(filePath, result.uploadUrl, { ...result, useChunks: true, chunkSize: 4 * 1024 * 1024 // 4MB可根据需要调整 }) console.log(分片上传成功下载地址:, result.downloadUrl) return result.downloadUrl } catch (error) { console.error(分片上传失败:, error) throw error } }分片上传参数说明参数名类型说明useChunksboolean是否使用分片上传设置为true启用分片上传chunkSizenumber分片大小单位为字节默认为4MB (4 * 1024 * 1024)当文件大小超过设定的分片大小时系统会自动将文件分成多个块进行上传并在上传完成后自动合并。需要说明的是在doUpload的七牛分支中分片大小与阈值在 Rust 端固定为 4MBconst chunk_size: u64 4 * 1024 * 1024前端传入的useChunks/chunkSize主要在浏览器直传路径uploadToQiniuWithChunks中生效。分片上传原理与优势分片上传流程七牛云分片上传基于七牛云的分块上传 API 实现主要流程如下将大文件分割成固定大小的数据块使用/mkblk/blocksize接口上传第一个数据块使用/bput/ctx/offset接口上传后续数据块所有分片上传完成后使用/mkfile/filesize/key/encodedKey接口将所有数据块合并成完整文件Rust 端实现深度解析分片上传的核心逻辑位于 src-tauri/src/command/upload_command.rs 的qiniu_resumable_upload函数可作为理解七牛分片 API 的完整参考实现路径解析resolve_upload_path支持绝对路径与基于AppCache/AppData的相对路径upload_command.rs循环上传块while transferred total循环内每次读取至多 4MB调用POST {domain}/mkblk/{len}请求头为Authorization: UpToken {token}、Content-Type: application/octet-stream响应解析出ctx存入contexts向量upload_command.rs合并文件所有块上传完成后将ctx列表以逗号拼接作为请求体调用POST {domain}/mkfile/{total}/key/{encoded_key}encoded_key为对象 Key 的 base64 编码最终从响应中取出keyupload_command.rs对象 Key 生成build_qiniu_key定义了三种命名策略upload_command.rs超过 4MB 阈值{storagePrefix 或 scene}/{时间戳}_{文件名}启用去重且未超阈值{scene}/{account}/{md5}.{suffix}MD5 在分块读取的同时增量计算兜底{scene}/{时间戳}_{文件名}进度回传每个分片完成后通过ChannelUploadProgressPayload发送progress_total与total前端据此计算百分比。分片上传的主要优势支持断点续传上传中断后可以从已上传的部分继续每个分片的ctx是独立凭证可单独重试提高上传成功率大文件分成小块上传减少单次传输失败的风险优化网络利用率可以更好地利用网络带宽提供上传进度反馈可以精确显示每个分片的上传进度建议在以下情况使用分片上传上传大于 10MB 的文件网络环境不稳定需要显示精确上传进度的场景浏览器直传路径除 Rust 端实现外src/hooks/useUpload.ts 中的uploadToQiniuWithChunks提供了浏览器环境如 Android WebView下的分片直传实现按chunkSize默认 4MB计算分片总数逐片file.slice(start, end)后 POST 到{domain}/mkblk/{currentChunkSize}全部完成后用contexts.join(,)作为 body 调用{domain}/mkfile/{totalSize}/key/{btoa(key)}并同步更新progress.value。同时uploadFileuseUpload.ts内置了 4MB 阈值判断文件大于阈值自动走分片上传否则走普通uploadToQiniuFormData token key。在消息策略中使用HuLa 的消息策略已集成七牛云上传功能。消息策略接口定义在 src/strategy/MessageStrategy.ts每个策略实现uploadFile(path, options)与doUpload(path, uploadUrl, options)两个方法通过传递provider选项指定上传方式import { messageStrategyMap } from /strategy/MessageStrategy import { MsgEnum } from /enums import { UploadProviderEnum } from /hooks/useUpload // 获取图片消息策略 const imageStrategy messageStrategyMap[MsgEnum.IMAGE] // 使用七牛云上传 async function uploadImageWithQiniu(path: string) { const result await imageStrategy.uploadFile(path, { provider: UploadProviderEnum.QINIU }) // 执行上传 await imageStrategy.doUpload(path, result.uploadUrl, result) return result.downloadUrl } // 使用七牛云分片上传大图片 async function uploadLargeImageWithQiniu(path: string) { const result await imageStrategy.uploadFile(path, { provider: UploadProviderEnum.QINIU }) // 执行分片上传 await imageStrategy.doUpload(path, result.uploadUrl, { ...result, useChunks: true, chunkSize: 2 * 1024 * 1024 // 2MB分片 }) return result.downloadUrl }从源码看具体消息策略的uploadFile会默认回退到UploadProviderEnum.QINIU如provider: options?.provider || UploadProviderEnum.QINIUdoUpload内部透传enableDeduplication: true并最终委托给useUpload的doUpload。视频策略还额外实现了doUploadThumbnail用于把封面缩略图写入临时文件后再走同一套上传链路MessageStrategy.ts。配置七牛云要使用七牛云上传功能需要在后端配置七牛云的相关参数在七牛云控制台创建存储空间Bucket获取 AccessKey 和 SecretKey配置后端服务实现获取上传 Token 的接口后端需要提供以下接口GET /api/qiniu/token返回格式{ token: 七牛云上传Token, domain: 七牛云存储域名, storagePrefix: 存储前缀 }前端通过getQiniuToken({ scene, fileName })调用该接口对应ImUrlEnum.GET_QINIU_TOKEN场景参数会作为查询参数传给后端便于后端按场景签发 Token 与对象 Key 前缀。测试方法在七牛云控制台创建存储空间Bucket获取 AccessKey 和 SecretKey配置后端服务实现获取上传 Token 的接口使用 HuLa 提供的上传接口进行测试可分别覆盖以下场景验证功能完整性小文件4MB普通上传确认返回的downloadUrl可访问大文件4MB分片上传观察进度回传是否连续、合并后文件完整性聊天、表情、头像三种UploadSceneEnum场景的对象 Key 目录是否正确不传provider时是否回落到后端STORAGE_PROVIDER指定的默认存储。注意事项七牛云上传需要有效的 TokenToken 有时效性过期后需要重新获取上传大文件时请确保网络稳定在生产环境中建议配置 HTTPS 域名确保数据传输安全七牛云存储有容量和流量限制请根据实际需求选择合适的套餐分片上传时建议根据网络环境和文件大小调整分片大小一般推荐 1MB-4MB对于特别大的文件如视频强烈建议使用分片上传功能单文件默认上限为 500MBMax 500见 useUpload.ts超出会提示「文件大小不能超过 500MB」若启用文件去重enableDeduplication文件名将基于文件 MD5 哈希生成相同内容仅需存储一份但去重模式仅对小文件≤4MB 阈值生效扩展阅读七牛上传凭证获取工具src/utils/ImRequestUtils.tsRust 端上传命令注册src-tauri/src/lib.rsTauri 命令与 URL 枚举定义src/enums/index.ts消息策略抽象与实现src/strategy/MessageStrategy.ts头像上传场景集成src/hooks/useAvatarUpload.ts【免费下载链接】HuLa A cross-platform instant messaging desktop application with exceptional performance built on Rust Vue3, compatible with Windows, macOS, Linux, Android, and iOS一款基于RustVue3极致性能的跨平台即时通讯桌面应用兼容Windows、MacOS、Linux、Android、IOS项目地址: https://gitcode.com/GitHub_Trending/hu/HuLa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考