@@ -5,20 +5,18 @@ const tinify = require('tinify');
55const path = require ( 'path' ) ;
66const fs = require ( 'fs' ) ;
77const mime = require ( 'mime-types' ) ;
8+ const sharp = require ( 'sharp' ) ;
89const { checkAndCreateTable } = require ( './utils/checkAndCreateTable' ) ;
910const pool = require ( './utils/db' ) ;
11+ const { appendSuffixToFilename } = require ( './utils/appendSuffixToFilename' ) ;
1012require ( 'dotenv' ) . config ( { path : '.env.local' } ) ;
1113
1214const app = new Koa ( ) ;
1315const router = new Router ( ) ;
1416
15- // TinyPNG API 密钥
1617tinify . key = process . env . TINIFY_KEY ;
1718
18- // Tinify 支持的文件格式
1919const tinifySupportedMimeTypes = [ 'image/jpeg' , 'image/png' , 'image/webp' ] ;
20-
21- // 所有图片格式
2220const imageMimeTypes = [
2321 'image/jpeg' ,
2422 'image/png' ,
@@ -30,10 +28,8 @@ const imageMimeTypes = [
3028 'image/svg+xml'
3129] ;
3230
33- // 设置静态文件服务目录
3431app . use ( require ( 'koa-static' ) ( path . join ( __dirname , 'public' ) ) ) ;
3532
36- // 创建必要的目录
3733const createDirectories = ( ) => {
3834 const dirs = [
3935 path . join ( __dirname , 'provisional' ) ,
@@ -48,7 +44,6 @@ const createDirectories = () => {
4844
4945createDirectories ( ) ;
5046
51- // 配置 koaBody 中间件处理文件上传
5247app . use (
5348 koaBody ( {
5449 multipart : true ,
@@ -59,36 +54,44 @@ app.use(
5954 } )
6055) ;
6156
62- // 文件上传和压缩的路由
6357router . post ( '/upload' , async ( ctx ) => {
6458 const connection = await pool . getConnection ( ) ;
6559 try {
66- // 获取上传的文件
6760 const files = ctx . request . files . file ;
6861 const fileList = Array . isArray ( files ) ? files : [ files ] ;
6962 const responses = [ ] ;
7063
71- // 检查是否需要压缩
7264 const compress = ctx . query . compress !== 'false' ; // 默认压缩
73- // 检查是否保留临时文件
7465 const keepTemp = ctx . query . keepTemp === 'true' ; // 默认不保留临时文件
75- // 获取请求的返回类型
66+ const isThumb = ctx . query . isThumb === 'true' ;
7667 const responseType = ctx . query . type ;
7768
7869 for ( const file of fileList ) {
79- // 获取文件的 MIME 类型
8070 const mimeType = mime . lookup ( file . filepath ) ;
8171
82- // 生成输出文件路径
8372 const outputFilePath = path . join (
8473 __dirname ,
8574 'public' ,
8675 'files' ,
8776 path . basename ( file . filepath )
8877 ) ;
78+ let outputFileThumbPath = null ;
79+ if ( isThumb && imageMimeTypes . includes ( mimeType ) ) {
80+ const fileThumbName = appendSuffixToFilename ( file . newFilename , 'thumb' ) ;
81+
82+ outputFileThumbPath = path . join (
83+ __dirname ,
84+ 'public' ,
85+ 'files' ,
86+ fileThumbName
87+ ) ;
88+
89+ await sharp ( file . filepath )
90+ . resize ( 200 , 200 ) // 调整图像大小为200x200像素
91+ . toFile ( outputFileThumbPath ) ;
92+ }
8993
9094 if ( compress && tinifySupportedMimeTypes . includes ( mimeType ) ) {
91- // 如果文件类型受 Tinify 支持且要求压缩,使用 TinyPNG API 压缩文件
9295 await tinify . fromFile ( file . filepath ) . toFile ( outputFilePath ) ;
9396 } else {
9497 // 如果不支持压缩或者不要求压缩,保留临时文件则复制文件,否则移动文件
@@ -99,42 +102,39 @@ router.post('/upload', async (ctx) => {
99102 }
100103 }
101104
102- // 构建文件 URL
103105 const fileUrl = `${ process . env . PUBLIC_NETWORK_DOMAIN } /files/${ path . basename (
104106 outputFilePath
105107 ) } `;
108+ const thumb_location = outputFileThumbPath ? `${ process . env . PUBLIC_NETWORK_DOMAIN } /files/${ path . basename ( outputFileThumbPath ) } ` : null ;
106109
107- // 插入文件信息到数据库
108110 await connection . execute (
109111 `INSERT INTO files (
110- filename, filesize, filelocation, created_by, is_public
111- ) VALUES (?, ?, ?, ?, ?)` ,
112+ filename, filesize, filelocation, created_by, is_public, thumb_location, is_delete
113+ ) VALUES (?, ?, ?, ?, ?, ?, ? )` ,
112114 [
113115 path . basename ( outputFilePath ) ,
114116 fs . statSync ( outputFilePath ) . size ,
115117 fileUrl ,
116118 ctx . query . createdBy || 'anonymous' ,
117- ctx . query . isPublic === 'true'
119+ ctx . query . isPublic === 'true' ,
120+ thumb_location ,
121+ 0 // 假设默认的 `is_delete` 状态为 0(未删除)
118122 ]
119123 ) ;
120124
121125 if ( responseType === 'md' && imageMimeTypes . includes ( mimeType ) ) {
122- // 如果文件是图片格式且请求类型是 'md',返回 Markdown 格式
123126 responses . push ( {
124127 filepath : ``
125128 } ) ;
126129 } else {
127- // 否则返回普通 URL
128130 responses . push ( { filepath : fileUrl } ) ;
129131 }
130132
131133 if ( ! keepTemp && fs . existsSync ( file . filepath ) ) {
132- // 删除临时上传文件
133134 fs . unlinkSync ( file . filepath ) ;
134135 }
135136 }
136137
137- // 返回响应
138138 ctx . body = fileList . length > 1 ? responses : responses [ 0 ] ;
139139 } catch ( error ) {
140140 ctx . status = 500 ;
@@ -144,15 +144,12 @@ router.post('/upload', async (ctx) => {
144144 }
145145} ) ;
146146
147- // 获取文件信息的路由
148147router . get ( '/files' , async ( ctx ) => {
149148 const connection = await pool . getConnection ( ) ;
150149 try {
151- // 获取分页参数
152150 const limit = parseInt ( ctx . query . limit , 10 ) || 10 ; // 每页数量,默认为 10
153151 const offset = parseInt ( ctx . query . offset , 10 ) || 0 ; // 偏移量,默认为 0
154152
155- // 执行查询
156153 const [ rows ] = await connection . execute (
157154 `SELECT * FROM files LIMIT ? OFFSET ?` ,
158155 [ limit , offset ]
@@ -167,7 +164,6 @@ router.get('/files', async (ctx) => {
167164 }
168165} ) ;
169166
170- // 启动服务器
171167app . use ( router . routes ( ) ) . use ( router . allowedMethods ( ) ) ;
172168
173169app . listen ( process . env . SERVER_PORT , async ( ) => {
0 commit comments