Nestjs 实现大文件分片上传

2023年 9月 28日 111.6k 0

在本文中,我们将探讨如何使用NestJS框架来实现大文件分片上传,并且将文件分片数据存储在Redis缓存中,以确保高效的上传和文件管理。这个方案对于需要处理大文件的应用程序,如云存储、媒体管理或数据备份系统,尤其有用。

仓库地址:github.com/ios-lcj/nod…

设计思路

接口设计

我们的系统将包括以下API端点:

  • createUploadSession: 创建上传会话,用于初始化文件上传。
  • upload: 上传文件分片,将大文件分成小块并逐个上传。
  • delete: 取消上传或删除已上传的文件。
  • merge: 合并文件,将所有文件分片组合成一个完整的文件。
  • files: 获取已上传的文件或文件信息。
  • 技术栈

  • NestJS: 用于构建后端应用程序的Node.js框架。
  • Multer: 用于处理文件上传的Node.js中间件。
  • Redis: 用于存储上传文件分片的缓存数据库。
  • TypeScript: 用于类型安全和代码可维护性。
  • 实现步骤

    以下是实现大文件分片上传的主要步骤:

    1. 创建Upload会话

    首先,我们需要一个接口来创建上传会话,这个接口将生成一个唯一的会话ID,用于标识上传的文件。在这个步骤中,我们将在Redis中创建一个新的缓存条目,以存储与该会话相关的数据,如文件名、文件大小等。

    POST /api/createUploadSession
    

    接口参数:

    • fileName: 上传的文件名。
    • fileSize: 文件的总大小。
    • fileHash: 文件的哈希值,用于校验文件完整性。
    • fileChunkNum: 文件的分片总数。

    实现步骤

  • 首先,我们需要检查指定的文件名是否已存在。如果已经存在相同的文件名,我们可以为新文件生成一个唯一的文件名,以避免冲突。

  • 然后,我们将创建一个新的会话ID,该ID可以是随机生成的唯一字符串,也可以使用某种唯一性规则生成。

  • 接下来,我们将在Redis中创建一个新的缓存条目,用于存储与该会话相关的数据。这个缓存条目应包含以下信息:

    • sessionId: 会话ID,用于标识该上传会话, 此处使用的是fileHash作为ID。
    • fileName: 文件名,可能是原始文件名或生成的唯一文件名。
    • fileSize: 文件的总大小。
    • fileHash: 文件的哈希值。
    • fileChunkNum: 文件的分片总数。
  • 设置一个有效期,以确保会话数据不会永久存储在Redis中,并且在超时后上传文件,将被禁止。

  • 最后,我们将返回新的文件信息和上传路径(用于后续分片上传),以便客户端继续上传文件分片。

  • 核心代码

    let newFileName = originalFileName;
    let count = 1;
    while (existsSync(path.join(File_Path, newFileName.toString()))) {
        newFileName = `${fileNameWithoutExtension} (${count})${fileExtension}`;
        count++;
    }
    const fileCacheKey = req.fileHash;
    const fileInfo = {
        ...req,
        fileName: newFileName,
        experience: File_Upload_Expiration,
        uploadPath: `/upload/${fileCacheKey}`
    }
    ​
    this.redisCacheService.set(fileCacheKey, fileInfo, File_Upload_Expiration);
    

    2. 上传文件分片

    接下来,我们需要一个接口来上传文件分片。客户端将根据会话ID逐个上传文件分片,并且每个分片都将存储在Redis中,使用会话ID和分片索引进行标识。

    POST /api/upload/:sessionId
    

    接口参数

    • fileHash: 文件的哈希值,用于校验文件完整性。
    • chunkHash: 分片的哈希值,用于校验分片完整性。
    • chunkIndex: 分片的索引,表示分片的顺序。
    • chunkSize: 分片的大小,确保与给定的文件大小相同。
    • file: 分片文件的实际数据。

    返回结果

    • 如果已完成所有分片的上传,则返回文件信息和文件的Url
    • 如果未完成所有分片的上传,则返回文件信息、已上传的分片索引和未上传的分片索引。

    接口功能

    上传文件分片接口是整个大文件分片上传流程中的重要环节,它需要执行以下功能:

  • 校验分片是否已上传: 接口会首先检查该分片是否已经上传,以防止重复上传。
  • 参数校验: 接口会验证上传的分片大小是否与给定文件的大小相同,同时会检查文件哈希值是否在Redis中有缓存条目,确保上传的分片与文件匹配。
  • 创建缓存文件: 上传的分片将被保存在缓存文件夹中,以便后续合并。
  • 保存分片信息到Redis: 分片的信息,如分片哈希值、分片索引等,将被存储在Redis中,用于跟踪已上传的分片。
  • 合并分片: 如果上传的分片是最后一个分片,接口将检查是否已完成所有分片的上传。如果未完成,则会返回文件信息、已上传的分片和未上传的分片。如果已完成,则会合并所有分片,并在合并完成后删除缓存的分片和Redis缓存。
  • 并发处理

    在合并文件时,接口利用Redis的锁机制来对文件上锁。如果有一个进程正在合并文件,其他接口将不再合并,而是直接返回文件信息。这确保只有一个进程完成文件的合并,防止并发冲突。

    // 检查文件是否正在合并
    const fileLockKey = fileInfo.fileHash + File_Lock;
    // 锁定文件
    const fileLock = await this.redisCacheService.lock(fileLockKey, File_Upload_Expiration);
    if (!fileLock) {
        return {
            ...fileInfo,
        }
    }
    ​
    // ... 合并操作
    ​
    // 合并完成后
    this.deleteFileRedisInfo(fileInfo)
    await this.redisCacheService.unLock(fileLockKey);
    

    3. 取消上传

    如果用户需要取消上传或删除已上传的文件,我们可以提供一个接口来处理此操作。这将从Redis中删除与会话ID相关的数据,并清理已上传的文件分片。

    DELETE /api/upload/delete/:sessionId
    

    4. 合并文件

    一旦所有文件分片都已上传,我们需要一个接口来合并它们并生成完整的文件。这将涉及读取Redis中的所有文件分片并将它们组合成一个文件。

    原理与分片上传完成时自动合并相同

    POST /api/upload/mergeFile/:sessionId
    

    5. 获取文件

    最后,我们需要一个接口来获取已上传的文件。这个接口可以用于下载文件。

    GET /api/upload/getFile/:sessionId
    

    Redis缓存

    为了实现文件分片的高效管理,我们将使用Redis作为缓存数据库。每个会话ID将对应一个Redis缓存条目,其中包含文件的元数据和分片数据。这将确保上传文件分片时的快速访问和合并文件时的高效性能。

    总结

    在本文中,我们讨论了如何使用NestJS框架以及Redis缓存来实现大文件分片上传。这个方案能够确保文件上传的高效性和可靠性,特别适用于需要处理大文件的应用程序。通过正确设计接口和使用Redis缓存,我们可以构建一个可扩展和可靠的文件上传系统。

    要实施这个方案,你可以使用NestJS来构建后端应用程序,并结合Multer来处理文件上传,同时使用Redis来存储文件分片数据。这将为你的应用程序提供一个强大的文件管理系统,能够处理大型文件的上传和管理需求。

    最后贴上仓库:github.com/ios-lcj/nod…

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论