post.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
  2. import { FileService } from '@/server/modules/files/file.service';
  3. import { MinioService } from '@/server/modules/files/minio.service';
  4. import { ErrorSchema } from '@/server/utils/errorHandler';
  5. import { AppDataSource } from '@/server/data-source';
  6. import { AuthContext } from '@/server/types/context';
  7. import { authMiddleware } from '@/server/middleware/auth.middleware';
  8. // 创建分片上传策略请求Schema
  9. const CreateMultipartUploadPolicyDto = z.object({
  10. fileKey: z.string().openapi({
  11. description: '文件键名',
  12. example: 'documents/report.pdf'
  13. }),
  14. totalSize: z.coerce.number().int().positive().openapi({
  15. description: '文件总大小(字节)',
  16. example: 10485760
  17. }),
  18. partSize: z.coerce.number().int().positive().openapi({
  19. description: '分片大小(字节)',
  20. example: 5242880
  21. }),
  22. type: z.string().max(50).nullable().optional().openapi({
  23. description: '文件类型',
  24. example: 'application/pdf'
  25. })
  26. });
  27. // 创建分片上传策略路由定义
  28. const createMultipartUploadPolicyRoute = createRoute({
  29. method: 'post',
  30. path: '/',
  31. middleware: [authMiddleware],
  32. request: {
  33. body: {
  34. content: {
  35. 'application/json': { schema: CreateMultipartUploadPolicyDto }
  36. }
  37. }
  38. },
  39. responses: {
  40. 200: {
  41. description: '生成分片上传策略成功',
  42. content: {
  43. 'application/json': {
  44. schema: z.object({
  45. uploadId: z.string().openapi({
  46. description: '分片上传ID',
  47. example: '123e4567-e89b-12d3-a456-426614174000'
  48. }),
  49. bucket: z.string().openapi({
  50. description: '存储桶名称',
  51. example: 'my-bucket'
  52. }),
  53. key: z.string().openapi({
  54. description: '文件键名',
  55. example: 'documents/report.pdf'
  56. }),
  57. host: z.string().openapi({
  58. description: 'MinIO主机地址',
  59. example: 'minio.example.com'
  60. }),
  61. partUrls: z.array(z.string()).openapi({
  62. description: '分片上传URL列表',
  63. example: [
  64. 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=1',
  65. 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=2'
  66. ]
  67. })
  68. })
  69. }
  70. }
  71. },
  72. 400: {
  73. description: '请求参数错误',
  74. content: { 'application/json': { schema: ErrorSchema } }
  75. },
  76. 500: {
  77. description: '服务器错误',
  78. content: { 'application/json': { schema: ErrorSchema } }
  79. }
  80. }
  81. });
  82. // 创建文件服务实例
  83. const fileService = new FileService(AppDataSource);
  84. // 创建路由实例
  85. const app = new OpenAPIHono<AuthContext>().openapi(createMultipartUploadPolicyRoute, async (c) => {
  86. try {
  87. const data = await c.req.json();
  88. // 计算分片数量
  89. const partCount = Math.ceil(data.totalSize / data.partSize);
  90. const result = await fileService.createMultipartUploadPolicy(data, partCount);
  91. return c.json({
  92. uploadId: result.uploadId,
  93. bucket: result.bucket,
  94. key: result.key,
  95. host: `${process.env.MINIO_USE_SSL ? 'https' : 'http'}://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}`,
  96. partUrls: result.uploadUrls
  97. }, 200);
  98. } catch (error) {
  99. const message = error instanceof Error ? error.message : '生成分片上传策略失败';
  100. return c.json({ code: 500, message }, 500);
  101. }
  102. });
  103. export default app;