post.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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. name: z.string().max(255).openapi({
  27. description: '文件名称',
  28. example: '项目计划书.pdf'
  29. })
  30. });
  31. // 创建分片上传策略路由定义
  32. const createMultipartUploadPolicyRoute = createRoute({
  33. method: 'post',
  34. path: '/',
  35. middleware: [authMiddleware],
  36. request: {
  37. body: {
  38. content: {
  39. 'application/json': { schema: CreateMultipartUploadPolicyDto }
  40. }
  41. }
  42. },
  43. responses: {
  44. 200: {
  45. description: '生成分片上传策略成功',
  46. content: {
  47. 'application/json': {
  48. schema: z.object({
  49. uploadId: z.string().openapi({
  50. description: '分片上传ID',
  51. example: '123e4567-e89b-12d3-a456-426614174000'
  52. }),
  53. bucket: z.string().openapi({
  54. description: '存储桶名称',
  55. example: 'my-bucket'
  56. }),
  57. key: z.string().openapi({
  58. description: '文件键名',
  59. example: 'documents/report.pdf'
  60. }),
  61. host: z.string().openapi({
  62. description: 'MinIO主机地址',
  63. example: 'minio.example.com'
  64. }),
  65. partUrls: z.array(z.string()).openapi({
  66. description: '分片上传URL列表',
  67. example: [
  68. 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=1',
  69. 'https://minio.example.com/my-bucket/documents/report.pdf?uploadId=123e4567-e89b-12d3-a456-426614174000&partNumber=2'
  70. ]
  71. })
  72. })
  73. }
  74. }
  75. },
  76. 400: {
  77. description: '请求参数错误',
  78. content: { 'application/json': { schema: ErrorSchema } }
  79. },
  80. 500: {
  81. description: '服务器错误',
  82. content: { 'application/json': { schema: ErrorSchema } }
  83. }
  84. }
  85. });
  86. // 创建文件服务实例
  87. const fileService = new FileService(AppDataSource);
  88. // 创建路由实例
  89. const app = new OpenAPIHono<AuthContext>().openapi(createMultipartUploadPolicyRoute, async (c) => {
  90. try {
  91. const data = await c.req.json();
  92. const user = c.get('user');
  93. // 计算分片数量
  94. const partCount = Math.ceil(data.totalSize / data.partSize);
  95. const result = await fileService.createMultipartUploadPolicy({
  96. ...data,
  97. uploadUserId: user.id
  98. }, partCount);
  99. return c.json({
  100. uploadId: result.uploadId,
  101. bucket: result.bucket,
  102. key: result.key,
  103. host: `${process.env.MINIO_USE_SSL ? 'https' : 'http'}://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}`,
  104. partUrls: result.uploadUrls
  105. }, 200);
  106. } catch (error) {
  107. const message = error instanceof Error ? error.message : '生成分片上传策略失败';
  108. return c.json({ code: 500, message }, 500);
  109. }
  110. });
  111. export default app;