yuanjiajun commited on
Commit ·
c84f41e
1
Parent(s): f6d320c
feat: 变量
Browse files- src/controllers/index.ts +2 -2
- src/middleware/error-handler.ts +11 -0
- src/middleware/index.ts +1 -0
- src/service/article-service.ts +10 -0
- src/service/image-service.ts +40 -0
- src/service/index.ts +2 -0
- src/utils/common.ts +7 -0
- src/utils/file-utils.ts +72 -0
- src/utils/index.ts +2 -0
src/controllers/index.ts
CHANGED
|
@@ -10,8 +10,8 @@ const combinedRouter = new Router();
|
|
| 10 |
// 组合根路由
|
| 11 |
combinedRouter.use(RootController.routes()).use(RootController.allowedMethods());
|
| 12 |
|
| 13 |
-
//
|
| 14 |
-
|
| 15 |
|
| 16 |
// // 组合静态资源接口路由
|
| 17 |
// combinedRouter.use(StaticResourceController.routes()).use(StaticResourceController.allowedMethods());
|
|
|
|
| 10 |
// 组合根路由
|
| 11 |
combinedRouter.use(RootController.routes()).use(RootController.allowedMethods());
|
| 12 |
|
| 13 |
+
// 组合图像相关接口路由
|
| 14 |
+
combinedRouter.use(ImageController.routes()).use(ImageController.allowedMethods());
|
| 15 |
|
| 16 |
// // 组合静态资源接口路由
|
| 17 |
// combinedRouter.use(StaticResourceController.routes()).use(StaticResourceController.allowedMethods());
|
src/middleware/error-handler.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Context } from 'koa';
|
| 2 |
+
|
| 3 |
+
// 统一的错误处理中间件函数
|
| 4 |
+
export const handleControllerError = async (ctx: Context, error: any) => {
|
| 5 |
+
console.error('Controller Error:', error);
|
| 6 |
+
|
| 7 |
+
ctx.status = error.statusCode || 500;
|
| 8 |
+
ctx.body = {
|
| 9 |
+
error: error.message,
|
| 10 |
+
};
|
| 11 |
+
};
|
src/middleware/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export * from './error-handler';
|
src/service/article-service.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import axios from 'axios';
|
| 2 |
+
import fs from 'fs';
|
| 3 |
+
import htmlDocx from 'html-docx-js';
|
| 4 |
+
import path from 'path';
|
| 5 |
+
|
| 6 |
+
import { delay } from '@/utils';
|
| 7 |
+
|
| 8 |
+
const uploadDir = path.join(__dirname, '../../uploads');
|
| 9 |
+
|
| 10 |
+
export const processArticleServe = async (data: { title: string; content: string; config: { output: string; hfApiKey: string } }) => {};
|
src/service/image-service.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import path from 'path';
|
| 2 |
+
import sharp from 'sharp';
|
| 3 |
+
|
| 4 |
+
import { createUploadDir, getFileStream } from '../utils/file-utils';
|
| 5 |
+
|
| 6 |
+
const uploadDir = path.join(__dirname, '../uploads');
|
| 7 |
+
|
| 8 |
+
// 处理接收二进制数据并转换为JPG图像的业务逻辑
|
| 9 |
+
export const processImageServe = async (data: any) => {
|
| 10 |
+
if (!Buffer.isBuffer(data)) {
|
| 11 |
+
throw new Error('Expected application/octet-stream data');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
const outputFilename = `image-${Date.now()}.jpg`;
|
| 15 |
+
const outputPath = path.join(uploadDir, outputFilename);
|
| 16 |
+
|
| 17 |
+
// 使用 sharp 将 Buffer 转换为 JPG 并保存到本地文件系统
|
| 18 |
+
await sharp(data).toFormat('jpg').toFile(outputPath);
|
| 19 |
+
|
| 20 |
+
// 返回图像的文件路径
|
| 21 |
+
return {
|
| 22 |
+
message: 'Buffer data received and converted to JPG',
|
| 23 |
+
imagePath: outputFilename,
|
| 24 |
+
};
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
// 获取生成的图像文件的业务逻辑
|
| 28 |
+
export const getGeneratedFile = async (filename: string, type: string) => {
|
| 29 |
+
const uploadDir = createUploadDir();
|
| 30 |
+
const fileStream = getFileStream(filename, uploadDir);
|
| 31 |
+
|
| 32 |
+
if (fileStream) {
|
| 33 |
+
return {
|
| 34 |
+
type,
|
| 35 |
+
stream: fileStream,
|
| 36 |
+
};
|
| 37 |
+
} else {
|
| 38 |
+
throw new Error('File not found');
|
| 39 |
+
}
|
| 40 |
+
};
|
src/service/index.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export * from './article-service';
|
| 2 |
+
export * from './image-service';
|
src/utils/common.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import util from 'util';
|
| 2 |
+
const setTimeoutPromise = util.promisify(setTimeout);
|
| 3 |
+
|
| 4 |
+
export async function delay(time: number) {
|
| 5 |
+
await setTimeoutPromise(time);
|
| 6 |
+
console.log(`延迟${time}毫秒后执行`);
|
| 7 |
+
}
|
src/utils/file-utils.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fs from 'fs';
|
| 2 |
+
import os from 'os';
|
| 3 |
+
import path from 'path';
|
| 4 |
+
import sharp from 'sharp';
|
| 5 |
+
|
| 6 |
+
// 创建上传文件夹,如果不存在
|
| 7 |
+
export const createUploadDir = (): string => {
|
| 8 |
+
const uploadDir = path.join(__dirname, '../../uploads');
|
| 9 |
+
if (!fs.existsSync(uploadDir)) {
|
| 10 |
+
fs.mkdirSync(uploadDir);
|
| 11 |
+
}
|
| 12 |
+
return uploadDir;
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
// 使用 sharp 将 Buffer 转换为 JPG 并保存到本地文件系统
|
| 16 |
+
export const bufferToJpg = async (data: Buffer, outputFilename: string, uploadDir: string): Promise<string> => {
|
| 17 |
+
const outputPath = path.join(uploadDir, outputFilename);
|
| 18 |
+
await sharp(data).toFormat('jpg').toFile(outputPath);
|
| 19 |
+
return outputFilename;
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
// 检查文件是否存在并返回文件流(如果存在)
|
| 23 |
+
export const getFileStream = (filename: string, uploadDir: string) => {
|
| 24 |
+
const filePath = path.join(uploadDir, filename);
|
| 25 |
+
if (fs.existsSync(filePath)) {
|
| 26 |
+
return fs.createReadStream(filePath);
|
| 27 |
+
}
|
| 28 |
+
return null;
|
| 29 |
+
};
|
| 30 |
+
|
| 31 |
+
export function bufferToBase64ImageSrc(buffer: Buffer, mimeType: string): string {
|
| 32 |
+
// 将 Buffer 转换为 base64 字符串
|
| 33 |
+
const base64 = buffer.toString('base64');
|
| 34 |
+
|
| 35 |
+
// 构建 base64 图片源
|
| 36 |
+
return `data:${mimeType};base64,${base64}`;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
export async function downloadDocx(url: string, fileName: string) {
|
| 40 |
+
try {
|
| 41 |
+
// 发起 GET 请求下载文件
|
| 42 |
+
const response = await fetch(url);
|
| 43 |
+
if (!response.ok) {
|
| 44 |
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
| 45 |
+
}
|
| 46 |
+
// 确保响应头中的 Content-Type 为 DOCX 文件
|
| 47 |
+
const contentType = response.headers.get('content-type');
|
| 48 |
+
if (contentType !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
| 49 |
+
throw new Error(`Unexpected content type: ${contentType}`);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// 将响应体转换为 Buffer
|
| 53 |
+
const blob = await response.blob();
|
| 54 |
+
// 将 Blob 转换为 ArrayBuffer
|
| 55 |
+
const arrayBuffer = await blob.arrayBuffer();
|
| 56 |
+
// 将 ArrayBuffer 转换为 Buffer
|
| 57 |
+
const buffer = Buffer.from(arrayBuffer);
|
| 58 |
+
// 获取用户的桌面路径
|
| 59 |
+
const desktopPath = path.join(os.homedir(), 'Desktop');
|
| 60 |
+
if (!fs.existsSync(desktopPath)) {
|
| 61 |
+
throw new Error('Desktop path does not exist. Please ensure you have a Desktop directory.');
|
| 62 |
+
}
|
| 63 |
+
const filePath = path.join(desktopPath, fileName);
|
| 64 |
+
|
| 65 |
+
// 将 Buffer 写入到文件
|
| 66 |
+
fs.writeFileSync(filePath, buffer);
|
| 67 |
+
|
| 68 |
+
console.log(`DOCX 文件已保存到 ${filePath}`);
|
| 69 |
+
} catch (error) {
|
| 70 |
+
console.error('下载文件时出错:', error);
|
| 71 |
+
}
|
| 72 |
+
}
|
src/utils/index.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export * from './common';
|
| 2 |
+
export * from './file-utils';
|