简介:本文详细解析如何在NestJS中调用百度网盘API,涵盖环境配置、鉴权机制、核心接口实现及错误处理,提供完整代码示例与最佳实践。
在构建企业级文件管理系统时,集成第三方云存储服务成为刚需。百度网盘开放平台提供的API接口(如文件上传/下载、目录管理、分享链接生成等)可显著降低开发成本。NestJS作为基于TypeScript的后端框架,其模块化架构和依赖注入特性非常适合封装第三方服务。本方案通过构建独立的服务模块,实现与百度网盘API的无缝对接。
npm i -g @nestjs/clinest new baidu-drive-integrationcd baidu-drive-integrationnpm install axios @nestjs/config
百度网盘API采用OAuth2.0鉴权,需通过以下步骤获取access_token:
// src/auth/auth.service.tsimport { Injectable } from '@nestjs/common';import axios from 'axios';import { ConfigService } from '@nestjs/config';@Injectable()export class AuthService {constructor(private configService: ConfigService) {}async getAccessToken(): Promise<string> {const url = 'https://openapi.baidu.com/oauth/2.0/token';const params = {grant_type: 'client_credentials',client_id: this.configService.get('BAIDU_API_KEY'),client_secret: this.configService.get('BAIDU_SECRET_KEY'),};try {const response = await axios.get(url, { params });return response.data.access_token;} catch (error) {throw new Error(`Auth failed: ${error.message}`);}}}
# .envBAIDU_API_KEY=your_api_keyBAIDU_SECRET_KEY=your_secret_key
// src/baidu-drive/baidu-drive.service.tsimport { Injectable } from '@nestjs/common';import axios from 'axios';import { AuthService } from '../auth/auth.service';@Injectable()export class BaiduDriveService {constructor(private authService: AuthService) {}async uploadFile(filePath: string, path: string): Promise<{ file_id: string }> {const accessToken = await this.authService.getAccessToken();const url = 'https://pan.baidu.com/rest/2.0/pcs/file';// 百度网盘API要求使用multipart/form-dataconst formData = new FormData();formData.append('method', 'upload');formData.append('access_token', accessToken);formData.append('path', path);formData.append('file', createReadStream(filePath));try {const response = await axios.post(url, formData, {headers: formData.getHeaders()});return response.data;} catch (error) {throw new Error(`Upload failed: ${error.response?.data?.error_msg || error.message}`);}}}
async listFiles(path: string = '/'): Promise<Array<{ path: string, size: number }>> {const accessToken = await this.authService.getAccessToken();const url = 'https://pan.baidu.com/rest/2.0/pcs/file';const params = {method: 'list',access_token: accessToken,path: encodeURIComponent(path)};const response = await axios.get(url, { params });return response.data.list;}
async getRapidUploadInfo(fileMd5: string, size: number): Promise<{ rapid_upload: boolean }> {const accessToken = await this.authService.getAccessToken();const url = 'https://pan.baidu.com/rest/2.0/pcs/file';const params = {method: 'precreate',access_token: accessToken,path: `/temp/${Date.now()}`,size: size,content-md5: fileMd5,isdir: 0};try {const response = await axios.post(url, null, { params });return response.data;} catch (error) {if (error.response?.data?.error_code === 31066) {// 文件已存在,可直接创建秒传链接return { rapid_upload: true };}throw error;}}
async batchOperations(operations: Array<{ method: string, params: object }>) {const accessToken = await this.authService.getAccessToken();const url = 'https://pan.baidu.com/rest/2.0/pcs/batch';const batchParams = {access_token: accessToken,requests: operations.map(op => ({method: op.method,...op.params}))};return axios.post(url, batchParams);}
// src/baidu-drive/exceptions/baidu-drive.exception.tsexport class BaiduDriveException extends HttpException {constructor(errorCode: number, message: string) {super({errorCode,message,documentation: 'https://developer.baidu.com/doc/PAN/s/9k3h7x6q2'}, HttpStatus.BAD_REQUEST);}}
async safeRequest<T>(requestFn: () => Promise<T>,maxRetries = 3): Promise<T> {let retryCount = 0;while (retryCount <= maxRetries) {try {return await requestFn();} catch (error) {if (retryCount === maxRetries) throw error;const delay = Math.min(1000 * Math.pow(2, retryCount), 5000);await new Promise(resolve => setTimeout(resolve, delay));retryCount++;}}}
@nestjs/cache-manager缓存access_token(默认有效期30天)slice参数)
// src/baidu-drive/baidu-drive.module.tsimport { Module } from '@nestjs/common';import { BaiduDriveService } from './baidu-drive.service';import { AuthService } from '../auth/auth.service';import { ConfigModule } from '@nestjs/config';@Module({imports: [ConfigModule],providers: [BaiduDriveService, AuthService],exports: [BaiduDriveService]})export class BaiduDriveModule {}
// src/baidu-drive/test/baidu-drive.service.spec.tsdescribe('BaiduDriveService', () => {let service: BaiduDriveService;const mockAuthService = { getAccessToken: jest.fn() };beforeEach(async () => {const module: TestingModule = await Test.createTestingModule({providers: [BaiduDriveService,{ provide: AuthService, useValue: mockAuthService }],}).compile();service = module.get<BaiduDriveService>(BaiduDriveService);});it('should upload file successfully', async () => {mockAuthService.getAccessToken.mockResolvedValue('test_token');// 模拟axios响应jest.spyOn(axios, 'post').mockResolvedValue({ data: { file_id: '123' } });const result = await service.uploadFile('/tmp/test.txt', '/test/');expect(result).toEqual({ file_id: '123' });});});
通过以上实现,开发者可以在NestJS应用中高效、安全地集成百度网盘API。实际开发中,建议根据业务需求进一步封装业务逻辑,例如实现文件版本控制、自动清理过期文件等高级功能。