diff --git a/src/common/file.ts b/src/common/file.ts index cab8abb0..aa1a019e 100644 --- a/src/common/file.ts +++ b/src/common/file.ts @@ -1,9 +1,7 @@ import fs from 'fs'; import { stat } from 'fs/promises'; import crypto, { randomUUID } from 'crypto'; -import util from 'util'; import path from 'node:path'; -import * as fileType from 'file-type'; import { solveProblem } from '@/common/helper'; export interface HttpDownloadOptions { @@ -15,7 +13,6 @@ type Uri2LocalRes = { success: boolean, errMsg: string, fileName: string, - ext: string, path: string } @@ -73,27 +70,6 @@ async function checkFile(path: string): Promise { // 如果文件存在,则无需做任何事情,Promise 解决(resolve)自身 } -export async function file2base64(path: string) { - const readFile = util.promisify(fs.readFile); - const result = { - err: '', - data: '', - }; - try { - try { - await checkFileExist(path, 5000); - } catch (e: any) { - result.err = e.toString(); - return result; - } - const data = await readFile(path); - result.data = data.toString('base64'); - } catch (err: any) { - result.err = err.toString(); - } - return result; -} - export function calculateFileMD5(filePath: string): Promise { return new Promise((resolve, reject) => { // 创建一个流式读取器 @@ -160,20 +136,6 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi return Buffer.from(buffer); } -export async function checkFileV2(filePath: string) { - try { - const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext; - if (ext) { - fs.renameSync(filePath, filePath + `.${ext}`); - filePath += `.${ext}`; - return { success: true, ext: ext, path: filePath }; - } - } catch (e) { - // log("获取文件类型失败", filePath,e.stack) - } - return { success: false, ext: '', path: filePath }; -} - export enum FileUriType { Unknown = 0, Local = 1, @@ -213,63 +175,32 @@ export async function checkUriType(Uri: string) { return { Uri: Uri, Type: FileUriType.Unknown }; } -export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise { +export async function uriToLocalFile(dir: string, uri: string): Promise { const { Uri: HandledUri, Type: UriType } = await checkUriType(uri); - //解析失败 - const tempName = randomUUID(); - if (!filename) filename = randomUUID(); + const filename = randomUUID(); + const filePath = path.join(dir, filename); - //解析Http和Https协议 - if (UriType == FileUriType.Unknown) { - return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' }; - } + switch (UriType) { + case FileUriType.Local: + const fileExt = path.extname(HandledUri); + const localFileName = path.basename(HandledUri, fileExt) + fileExt; + const tempFilePath = path.join(dir, filename + fileExt); + fs.copyFileSync(HandledUri, tempFilePath); + return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath }; - //解析File协议和本地文件 - if (UriType == FileUriType.Local) { - const fileExt = path.extname(HandledUri); - let filename = path.basename(HandledUri, fileExt); - filename += fileExt; - //复制文件到临时文件并保持后缀 - const filenameTemp = tempName + fileExt; - const filePath = path.join(dir, filenameTemp); - fs.copyFileSync(HandledUri, filePath); - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; - } + case FileUriType.Remote: + const buffer = await httpDownload(HandledUri); + fs.writeFileSync(filePath, buffer, { flag: 'wx' }); + return { success: true, errMsg: '', fileName: filename, path: filePath }; - //接下来都要有文件名 - if (UriType == FileUriType.Remote) { - const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname)); - if (pathInfo.name) { - const pathlen = 200 - dir.length - pathInfo.name.length; - filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断 - if (pathInfo.ext) { - filename += pathInfo.ext; - } - } - filename = filename.replace(/[/\\:*?"<>|]/g, '_'); - const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10); - const filePath = path.join(dir, tempName + fileExt); - const buffer = await httpDownload(HandledUri); - //没有文件就创建 - fs.writeFileSync(filePath, buffer, { flag: 'wx' }); - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; - } + case FileUriType.Base64: + const base64 = HandledUri.replace(/^base64:\/\//, ''); + const base64Buffer = Buffer.from(base64, 'base64'); + fs.writeFileSync(filePath, base64Buffer, { flag: 'wx' }); + return { success: true, errMsg: '', fileName: filename, path: filePath }; - //解析Base64 - if (UriType == FileUriType.Base64) { - const base64 = HandledUri.replace(/^base64:\/\//, ''); - const buffer = Buffer.from(base64, 'base64'); - let filePath = path.join(dir, filename); - let fileExt = ''; - fs.writeFileSync(filePath, buffer); - const { success, ext, path: fileTypePath } = await checkFileV2(filePath); - if (success) { - filePath = fileTypePath; - fileExt = ext; - filename = filename + '.' + ext; - } - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; + default: + return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' }; } - return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' }; -} +} \ No newline at end of file diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index 07860ca8..eb58c916 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -6,7 +6,6 @@ import { Peer, PicElement, PicSubType, - PicType, RawMessage, SendFileElement, SendPicElement, @@ -17,7 +16,7 @@ import path from 'path'; import fs from 'fs'; import fsPromises from 'fs/promises'; import { InstanceContext, NapCatCore, SearchResultItem } from '@/core'; -import * as fileType from 'file-type'; +import { fileTypeFromFile } from 'file-type'; import imageSize from 'image-size'; import { ISizeCalculationResult } from 'image-size/dist/types/interface'; import { RkeyManager } from '@/core/helper/rkey'; @@ -41,7 +40,7 @@ export class NTQQFileApi { this.rkeyManager = new RkeyManager([ 'https://rkey.napneko.icu/rkeys' ], - this.context.logger + this.context.logger ); } @@ -62,7 +61,7 @@ export class NTQQFileApi { async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) { const fileMd5 = await calculateFileMD5(filePath); - const extOrEmpty = (await fileType.fileTypeFromFile(filePath))?.ext; + let extOrEmpty = await fileTypeFromFile(filePath).then(e => e?.ext ?? '').catch(e => ''); const ext = extOrEmpty ? `.${extOrEmpty}` : ''; let fileName = `${path.basename(filePath)}`; if (fileName.indexOf('.') === -1) { @@ -81,6 +80,7 @@ export class NTQQFileApi { }); await this.copyFile(filePath, mediaPath); + console.log('copyFile', filePath, mediaPath); const fileSize = await this.getFileSize(filePath); return { md5: fileMd5, @@ -158,7 +158,7 @@ export class NTQQFileApi { let fileExt = 'mp4'; try { - const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext; + const tempExt = (await fileTypeFromFile(filePath))?.ext; if (tempExt) fileExt = tempExt; } catch (e) { this.context.logger.logError('获取文件类型失败', e); @@ -306,18 +306,18 @@ export class NTQQFileApi { element.elementType === ElementType.FILE ) { switch (element.elementType) { - case ElementType.PIC: + case ElementType.PIC: element.picElement!.sourcePath = elementResults[elementIndex]; - break; - case ElementType.VIDEO: + break; + case ElementType.VIDEO: element.videoElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.PTT: + break; + case ElementType.PTT: element.pttElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.FILE: + break; + case ElementType.FILE: element.fileElement!.filePath = elementResults[elementIndex]; - break; + break; } elementIndex++; } diff --git a/src/core/helper/msg.ts b/src/core/helper/msg.ts index e1d3992e..c28858d8 100644 --- a/src/core/helper/msg.ts +++ b/src/core/helper/msg.ts @@ -1,7 +1,7 @@ -import * as fileType from 'file-type'; +import { fileTypeFromFile } from 'file-type'; import { PicType } from '../types'; export async function getFileTypeForSendType(picPath: string): Promise { - const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg'; + const fileTypeResult = (await fileTypeFromFile(picPath))?.ext ?? 'jpg'; const picTypeMap: { [key: string]: PicType } = { //'webp': PicType.NEWPIC_WEBP, 'gif': PicType.NEWPIC_GIF, diff --git a/src/onebot/action/extends/OCRImage.ts b/src/onebot/action/extends/OCRImage.ts index 7f79eab4..dc39b9e0 100644 --- a/src/onebot/action/extends/OCRImage.ts +++ b/src/onebot/action/extends/OCRImage.ts @@ -1,6 +1,6 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import fs from 'fs'; import { Static, Type } from '@sinclair/typebox'; @@ -15,7 +15,7 @@ export class OCRImage extends OneBotAction { payloadSchema = SchemaData; async _handle(payload: Payload) { - const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image)); + const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image)); if (!success) { throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`); } diff --git a/src/onebot/action/extends/SetQQAvatar.ts b/src/onebot/action/extends/SetQQAvatar.ts index f8003fec..d5b656a1 100644 --- a/src/onebot/action/extends/SetQQAvatar.ts +++ b/src/onebot/action/extends/SetQQAvatar.ts @@ -1,7 +1,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import fs from 'node:fs/promises'; -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import { Static, Type } from '@sinclair/typebox'; const SchemaData = Type.Object({ @@ -14,7 +14,7 @@ export default class SetAvatar extends OneBotAction { actionName = ActionName.SetQQAvatar; payloadSchema = SchemaData; async _handle(payload: Payload): Promise { - const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); + const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file)); if (!success) { throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/SendGroupNotice.ts b/src/onebot/action/go-cqhttp/SendGroupNotice.ts index 998d711f..6dd8a343 100644 --- a/src/onebot/action/go-cqhttp/SendGroupNotice.ts +++ b/src/onebot/action/go-cqhttp/SendGroupNotice.ts @@ -1,4 +1,4 @@ -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { unlink } from 'node:fs/promises'; @@ -28,7 +28,7 @@ export class SendGroupNotice extends OneBotAction { const { path, success, - } = (await uri2local(this.core.NapCatTempPath, payload.image)); + } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image)); if (!success) { throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts index b9354dd5..41532109 100644 --- a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts +++ b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts @@ -1,6 +1,6 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -import { checkFileExistV2, uri2local } from '@/common/file'; +import { checkFileExistV2, uriToLocalFile } from '@/common/file'; import { Static, Type } from '@sinclair/typebox'; import fs from 'node:fs/promises'; const SchemaData = Type.Object({ @@ -15,7 +15,7 @@ export default class SetGroupPortrait extends OneBotAction { payloadSchema = SchemaData; async _handle(payload: Payload): Promise { - const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); + const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file)); if (!success) { throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/UploadGroupFile.ts b/src/onebot/action/go-cqhttp/UploadGroupFile.ts index 62e5cf70..3c44458a 100644 --- a/src/onebot/action/go-cqhttp/UploadGroupFile.ts +++ b/src/onebot/action/go-cqhttp/UploadGroupFile.ts @@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { ChatType, Peer } from '@/core/types'; import fs from 'fs'; -import { uri2local } from '@/common/file'; +import { uriToLocalFile } from '@/common/file'; import { SendMessageContext } from '@/onebot/api'; import { Static, Type } from '@sinclair/typebox'; @@ -25,7 +25,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction if (fs.existsSync(file)) { file = `file://${file}`; } - const downloadResult = await uri2local(this.core.NapCatTempPath, file); + const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file); const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString(), diff --git a/src/onebot/action/go-cqhttp/UploadPrivateFile.ts b/src/onebot/action/go-cqhttp/UploadPrivateFile.ts index 4633162e..aa555852 100644 --- a/src/onebot/action/go-cqhttp/UploadPrivateFile.ts +++ b/src/onebot/action/go-cqhttp/UploadPrivateFile.ts @@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { ChatType, Peer, SendFileElement } from '@/core/types'; import fs from 'fs'; -import { uri2local } from '@/common/file'; +import { uriToLocalFile } from '@/common/file'; import { SendMessageContext } from '@/onebot/api'; import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg'; import { Static, Type } from '@sinclair/typebox'; @@ -36,7 +36,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction