NestJS调用网盘API实战:百度网盘集成指南

NestJS调用网盘API实战:百度网盘集成指南

在开发企业级应用时,文件存储与管理是常见需求。主流网盘服务(如百度网盘)提供了丰富的API接口,支持文件上传、下载、分享等操作。本文将详细介绍如何在NestJS项目中调用此类网盘API,从环境准备、API鉴权到具体功能实现,提供完整的代码示例和最佳实践。

一、环境准备与API文档分析

1.1 注册开发者账号与创建应用

首先,需在网盘服务商的开放平台注册开发者账号,创建应用并获取API KeySecret Key。以某主流网盘服务为例,其开发者平台通常提供详细的API文档和SDK支持。

1.2 分析API文档

网盘API通常分为以下几类:

  • OAuth2.0鉴权接口:用于获取访问令牌(Access Token)
  • 文件管理接口:上传、下载、删除、移动文件
  • 分享管理接口:创建、取消文件分享链接
  • 用户信息接口:获取当前用户信息

以文件上传接口为例,典型请求参数包括:

  • access_token:鉴权令牌
  • path:文件在网盘中的路径
  • file:待上传的文件流

二、NestJS项目集成方案

2.1 安装依赖库

推荐使用axiosnestjs/axios模块进行HTTP请求:

  1. npm install @nestjs/axios axios

2.2 创建鉴权服务模块

  1. // auth.service.ts
  2. import { Injectable } from '@nestjs/common';
  3. import { HttpService } from '@nestjs/axios';
  4. import { firstValueFrom } from 'rxjs';
  5. @Injectable()
  6. export class AuthService {
  7. constructor(private readonly httpService: HttpService) {}
  8. async getAccessToken(apiKey: string, secretKey: string): Promise<string> {
  9. const url = 'https://openapi.example.com/oauth/token';
  10. const params = new URLSearchParams();
  11. params.append('grant_type', 'client_credentials');
  12. params.append('client_id', apiKey);
  13. params.append('client_secret', secretKey);
  14. const response = await firstValueFrom(
  15. this.httpService.post(url, params.toString(), {
  16. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  17. })
  18. );
  19. return response.data.access_token;
  20. }
  21. }

2.3 实现文件上传服务

  1. // file.service.ts
  2. import { Injectable } from '@nestjs/common';
  3. import { HttpService } from '@nestjs/axios';
  4. import { firstValueFrom } from 'rxjs';
  5. import { AuthService } from './auth.service';
  6. @Injectable()
  7. export class FileService {
  8. constructor(
  9. private readonly httpService: HttpService,
  10. private readonly authService: AuthService
  11. ) {}
  12. async uploadFile(
  13. filePath: string,
  14. destPath: string,
  15. apiKey: string,
  16. secretKey: string
  17. ): Promise<{ fileId: string; shareLink?: string }> {
  18. const accessToken = await this.authService.getAccessToken(apiKey, secretKey);
  19. const formData = new FormData();
  20. formData.append('path', destPath);
  21. formData.append('file', fs.createReadStream(filePath));
  22. const response = await firstValueFrom(
  23. this.httpService.post('https://api.example.com/file/upload', formData, {
  24. headers: {
  25. 'Authorization': `Bearer ${accessToken}`,
  26. // 其他必要头部
  27. },
  28. })
  29. );
  30. // 可选:创建分享链接
  31. if (needShare) {
  32. const shareResponse = await this.createShareLink(response.data.fileId, accessToken);
  33. return { ...response.data, shareLink: shareResponse.data.link };
  34. }
  35. return response.data;
  36. }
  37. private async createShareLink(fileId: string, accessToken: string) {
  38. return firstValueFrom(
  39. this.httpService.post(`https://api.example.com/share/create`, { fileId }, {
  40. headers: { 'Authorization': `Bearer ${accessToken}` }
  41. })
  42. );
  43. }
  44. }

三、最佳实践与注意事项

3.1 令牌缓存策略

避免频繁请求Access Token,建议实现缓存机制:

  1. // auth.service.ts 增强版
  2. @Injectable()
  3. export class AuthService {
  4. private tokenCache: { token: string; expiresAt: number } | null = null;
  5. async getAccessToken(apiKey: string, secretKey: string): Promise<string> {
  6. if (this.tokenCache && Date.now() < this.tokenCache.expiresAt - 300000) {
  7. return this.tokenCache.token; // 提前5分钟刷新
  8. }
  9. const response = await this.fetchNewToken(apiKey, secretKey);
  10. this.tokenCache = {
  11. token: response.data.access_token,
  12. expiresAt: Date.now() + response.data.expires_in * 1000,
  13. };
  14. return this.tokenCache.token;
  15. }
  16. }

3.2 错误处理与重试机制

  1. // utils/retry.ts
  2. export async function retry<T>(
  3. fn: () => Promise<T>,
  4. maxRetries = 3,
  5. delay = 1000
  6. ): Promise<T> {
  7. let lastError;
  8. for (let i = 0; i < maxRetries; i++) {
  9. try {
  10. return await fn();
  11. } catch (error) {
  12. lastError = error;
  13. if (i < maxRetries - 1) await new Promise(resolve => setTimeout(resolve, delay));
  14. delay *= 2; // 指数退避
  15. }
  16. }
  17. throw lastError;
  18. }

3.3 文件分片上传优化

对于大文件,建议使用分片上传:

  1. async uploadLargeFile(
  2. filePath: string,
  3. destPath: string,
  4. chunkSize = 5 * 1024 * 1024 // 5MB
  5. ) {
  6. const fileStat = await fs.stat(filePath);
  7. const totalChunks = Math.ceil(fileStat.size / chunkSize);
  8. for (let i = 0; i < totalChunks; i++) {
  9. const start = i * chunkSize;
  10. const end = Math.min(start + chunkSize, fileStat.size);
  11. const chunkStream = fs.createReadStream(filePath, { start, end });
  12. const formData = new FormData();
  13. formData.append('path', destPath);
  14. formData.append('chunk', i);
  15. formData.append('total', totalChunks);
  16. formData.append('file', chunkStream);
  17. await this.httpService.post('/upload/chunk', formData, {
  18. headers: { 'Authorization': `Bearer ${accessToken}` }
  19. }).toPromise();
  20. }
  21. await this.httpService.post('/upload/complete', { path: destPath });
  22. }

四、性能优化建议

  1. 并发控制:使用p-limit等库控制并发上传数
  2. 进度反馈:实现上传进度事件
  3. 断点续传:记录已上传分片信息
  4. 服务端验证:上传完成后验证文件完整性

五、安全注意事项

  1. 永远不要在前端代码中暴露Secret Key
  2. 实现严格的接口权限控制
  3. 对上传文件进行病毒扫描和类型检查
  4. 设置合理的API调用频率限制

总结

通过本文的实战指南,开发者可以掌握在NestJS项目中集成网盘API的核心技术:从基础的OAuth2.0鉴权到复杂的文件分片上传,从错误处理机制到性能优化策略。实际开发中,建议结合具体网盘服务商的API文档进行调整,并遵循最小权限原则设计安全方案。对于高并发场景,可考虑使用消息队列解耦上传流程,进一步提升系统稳定性。