file.controller.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
  2. import { FileService } from '../services/file.service'
  3. export class FileController {
  4. private fileService: FileService
  5. constructor(app: FastifyInstance) {
  6. this.fileService = new FileService(app)
  7. }
  8. /**
  9. * 上传ZIP文件
  10. */
  11. async uploadZip(request: FastifyRequest, reply: FastifyReply) {
  12. try {
  13. const data = await request.file()
  14. if (!data) {
  15. return reply.code(400).send({ message: '请选择要上传的ZIP文件' })
  16. }
  17. const buffer = await data.toBuffer()
  18. const filename = data.filename
  19. // 验证文件类型
  20. if (!filename.toLowerCase().endsWith('.zip')) {
  21. return reply.code(400).send({ message: '只支持ZIP格式的压缩包' })
  22. }
  23. const result = await this.fileService.uploadZip(buffer, filename, {
  24. maxSize: 100 * 1024 * 1024 // 100MB
  25. })
  26. return reply.send({
  27. message: 'ZIP文件上传成功',
  28. data: {
  29. ...result,
  30. dateFolder: new Date().toISOString().split('T')[0] // YYYY-MM-DD格式
  31. }
  32. })
  33. } catch (error) {
  34. return reply.code(500).send({
  35. message: 'ZIP文件上传失败',
  36. error: error instanceof Error ? error.message : '未知错误'
  37. })
  38. }
  39. }
  40. /**
  41. * 上传文件
  42. */
  43. async uploadFile(request: FastifyRequest, reply: FastifyReply) {
  44. try {
  45. const data = await request.file()
  46. if (!data) {
  47. return reply.code(400).send({ message: '请选择要上传的文件' })
  48. }
  49. const buffer = await data.toBuffer()
  50. const filename = data.filename
  51. const mimeType = data.mimetype
  52. const result = await this.fileService.uploadFile(buffer, filename, mimeType, {
  53. maxSize: 10 * 1024 * 1024 // 10MB
  54. })
  55. return reply.send({
  56. message: '文件上传成功',
  57. data: {
  58. ...result,
  59. dateFolder: new Date().toISOString().split('T')[0] // YYYY-MM-DD格式
  60. }
  61. })
  62. } catch (error) {
  63. return reply.code(500).send({
  64. message: '文件上传失败',
  65. error: error instanceof Error ? error.message : '未知错误'
  66. })
  67. }
  68. }
  69. /**
  70. * 上传图片
  71. */
  72. async uploadImage(request: FastifyRequest, reply: FastifyReply) {
  73. try {
  74. const data = await request.file()
  75. if (!data) {
  76. return reply.code(400).send({ message: '请选择要上传的图片' })
  77. }
  78. const buffer = await data.toBuffer()
  79. const filename = data.filename
  80. const result = await this.fileService.uploadImage(buffer, filename, {
  81. maxSize: 50 * 1024 * 1024,
  82. useSignedUrl: true
  83. })
  84. return reply.send({
  85. message: '图片上传成功',
  86. data: {
  87. ...result,
  88. dateFolder: new Date().toISOString().split('T')[0]
  89. }
  90. })
  91. } catch (error) {
  92. return reply.code(500).send({
  93. message: '图片上传失败',
  94. error: error instanceof Error ? error.message : '未知错误'
  95. })
  96. }
  97. }
  98. /**
  99. * 上传文档
  100. */
  101. async uploadDocument(request: FastifyRequest, reply: FastifyReply) {
  102. try {
  103. const data = await request.file()
  104. if (!data) {
  105. return reply.code(400).send({ message: '请选择要上传的文档' })
  106. }
  107. const buffer = await data.toBuffer()
  108. const filename = data.filename
  109. const result = await this.fileService.uploadDocument(buffer, filename, {
  110. maxSize: 50 * 1024 * 1024 // 50MB
  111. })
  112. return reply.send({
  113. message: '文档上传成功',
  114. data: {
  115. ...result,
  116. dateFolder: new Date().toISOString().split('T')[0] // YYYY-MM-DD格式
  117. }
  118. })
  119. } catch (error) {
  120. return reply.code(500).send({
  121. message: '文档上传失败',
  122. error: error instanceof Error ? error.message : '未知错误'
  123. })
  124. }
  125. }
  126. /**
  127. * 删除文件
  128. */
  129. async deleteFile(request: FastifyRequest<{ Params: { key: string } }>, reply: FastifyReply) {
  130. try {
  131. const { key } = request.params
  132. const success = await this.fileService.deleteFile(key)
  133. if (success) {
  134. return reply.send({
  135. message: '文件删除成功'
  136. })
  137. } else {
  138. return reply.code(404).send({
  139. message: '文件不存在或删除失败'
  140. })
  141. }
  142. } catch (error) {
  143. return reply.code(500).send({
  144. message: '文件删除失败',
  145. error: error instanceof Error ? error.message : '未知错误'
  146. })
  147. }
  148. }
  149. /**
  150. * 获取文件访问URL
  151. */
  152. async getFileUrl(request: FastifyRequest<{ Querystring: { key: string; expires?: number } }>, reply: FastifyReply) {
  153. try {
  154. const { key, expires = 3600 } = request.query
  155. const url = await this.fileService.getSignedUrl(key, expires)
  156. return reply.send({
  157. url,
  158. expires
  159. })
  160. } catch (error) {
  161. return reply.code(500).send({
  162. message: '获取文件URL失败',
  163. error: error instanceof Error ? error.message : '未知错误'
  164. })
  165. }
  166. }
  167. /**
  168. * 下载文件
  169. */
  170. async downloadFile(request: FastifyRequest<{ Body: { key: string } }>, reply: FastifyReply) {
  171. try {
  172. const { key } = request.body
  173. if (!key) {
  174. return reply.code(400).send({
  175. message: '文件key不能为空'
  176. })
  177. }
  178. // 检查文件是否存在
  179. const exists = await this.fileService.fileExists(key)
  180. if (!exists) {
  181. return reply.code(404).send({
  182. message: '文件不存在'
  183. })
  184. }
  185. // 获取文件信息
  186. const fileInfo = await this.fileService.getFileInfo(key)
  187. // 获取文件内容
  188. const fileBuffer = await this.fileService.downloadFile(key)
  189. // 设置响应头
  190. reply.header('Content-Type', fileInfo.res.headers['content-type'] || 'application/octet-stream')
  191. reply.header(
  192. 'Content-Disposition',
  193. `attachment; filename="${encodeURIComponent(key.split('/').pop() || 'file')}"`
  194. )
  195. reply.header('Content-Length', fileInfo.res.headers['content-length'] || fileBuffer.length)
  196. reply.header('Cache-Control', 'no-cache')
  197. // 发送文件内容
  198. return reply.send(fileBuffer)
  199. } catch (error) {
  200. return reply.code(500).send({
  201. message: '文件下载失败',
  202. error: error instanceof Error ? error.message : '未知错误'
  203. })
  204. }
  205. }
  206. }