diff --git a/src/store/modules/uploadStore.ts b/src/store/modules/uploadStore.ts index 0b5bc84..0ff4e32 100644 --- a/src/store/modules/uploadStore.ts +++ b/src/store/modules/uploadStore.ts @@ -2,11 +2,11 @@ import localforage from 'localforage'; interface UploadPredictResult { isAnime: boolean; - hasFace: boolean; objectArray: string[] | unknown[]; landscape: 'building' | 'forest' | 'glacier' | 'mountain' | 'sea' | 'street' | 'none'; isScreenshot: boolean; topCategory: string | undefined; + exif: object | null; } @@ -15,14 +15,13 @@ export const useUploadStore = defineStore( () => { const openUploadDrawer = ref(false); - const exifData = ref(); const predictResult = reactive({ isAnime: false, - hasFace: false, objectArray: [], landscape: 'none', isScreenshot: false, - topCategory: '' + topCategory: '', + exif: {} }); /** @@ -37,18 +36,17 @@ export const useUploadStore = defineStore( */ function clearPredictResult() { predictResult.isAnime = false; - predictResult.hasFace = false; predictResult.objectArray = []; predictResult.landscape = 'none'; predictResult.isScreenshot = false; predictResult.topCategory = ''; + predictResult.exif = {}; } return { openUploadDrawer, predictResult, - exifData, openUploadDrawerFn, clearPredictResult, }; diff --git a/src/utils/imageUtils/isScreenshot.ts b/src/utils/imageUtils/isScreenshot.ts index 6375f2f..d5363b5 100644 --- a/src/utils/imageUtils/isScreenshot.ts +++ b/src/utils/imageUtils/isScreenshot.ts @@ -16,8 +16,11 @@ async function isScreenshot(file) { 'screenshot', '屏幕截图', '截屏', 'Snip', 'Capture', 'Snapshot', '截图' ]; + // 获取文件的 MIME 类型 + const fileType = file.type.toLowerCase(); + try { - // 文件名匹配 + // 判断文件名是否包含截图相关关键词 const fileName = file.name.toLowerCase(); const matchesFilename = screenshotFilenameKeywords.some(keyword => fileName.includes(keyword.toLowerCase()) @@ -26,45 +29,48 @@ async function isScreenshot(file) { return true; // 如果文件名包含截图相关关键词,直接判断为截图 } - // 提取 EXIF 数据 - const exifData = await exifr.parse(file, ['ImageWidth', 'ImageHeight', 'Software', 'XResolution', 'YResolution']); - const {ImageWidth, ImageHeight, Software} = exifData || {}; + // 判断文件类型是否为支持的格式 + const isSupportedFormat = ['image/jpeg', 'image/tiff', 'image/heif', 'image/png'].includes(fileType); - // 如果图片没有宽高信息,直接返回 false - if (!ImageWidth || !ImageHeight) { - return false; - } + if (isSupportedFormat) { + // 如果是支持的格式,提取 EXIF 数据 + const exifData = await exifr.parse(file, ['ImageWidth', 'ImageHeight', 'Software', 'XResolution', 'YResolution']); + const {ImageWidth, ImageHeight, Software} = exifData || {}; - // 校验宽高比是否接近常见屏幕比例 - const aspectRatio = Math.max(ImageWidth, ImageHeight) / Math.min(ImageWidth, ImageHeight); - const matchesAspectRatio = commonAspectRatios.some( - (ratio) => Math.abs(ratio - aspectRatio) <= aspectRatioTolerance - ); - - // 如果宽高比匹配,进一步检查 - if (matchesAspectRatio) { - // 检查软件标记是否与截图工具相关 - const screenshotSoftwareKeywords = ['Screenshot', 'Snipping Tool', 'Screen Capture', 'Grab', 'Sketch']; - if (Software && screenshotSoftwareKeywords.some((keyword) => Software.includes(keyword))) { - return true; + // 如果图片没有宽高信息,直接返回 false + if (!ImageWidth || !ImageHeight) { + return false; } - // 检查文件大小(截图通常较小) - const fileSizeMB = file.size / (1024 * 1024); - if (fileSizeMB <= maxScreenshotSizeMB) { - return true; + // 校验宽高比是否接近常见屏幕比例 + const aspectRatio = Math.max(ImageWidth, ImageHeight) / Math.min(ImageWidth, ImageHeight); + const matchesAspectRatio = commonAspectRatios.some( + (ratio) => Math.abs(ratio - aspectRatio) <= aspectRatioTolerance + ); + + // 如果宽高比匹配,进一步检查 + if (matchesAspectRatio) { + // 检查软件标记是否与截图工具相关 + const screenshotSoftwareKeywords = ['Screenshot', 'Snipping Tool', 'Screen Capture', 'Grab', 'Sketch']; + if (Software && screenshotSoftwareKeywords.some((keyword) => Software.includes(keyword))) { + return true; + } + + // 检查文件大小(截图通常较小) + const fileSizeMB = file.size / (1024 * 1024); + if (fileSizeMB <= maxScreenshotSizeMB) { + return true; + } } } // 无 EXIF 数据或不匹配时,通过宽高比的容差检测 const img: any = await getImageDimensions(file); const imgAspectRatio = img.width / img.height; - const imgMatchesAspectRatio = commonAspectRatios.some( + return commonAspectRatios.some( (ratio) => Math.abs(ratio - imgAspectRatio) <= aspectRatioTolerance ); - return imgMatchesAspectRatio; - } catch (error) { console.error('判断截图时发生错误:', error); return false; diff --git a/src/views/Photograph/ImageUpload/ImageUpload.vue b/src/views/Photograph/ImageUpload/ImageUpload.vue index aa29f3c..7502404 100644 --- a/src/views/Photograph/ImageUpload/ImageUpload.vue +++ b/src/views/Photograph/ImageUpload/ImageUpload.vue @@ -21,6 +21,8 @@ :disabled="predicting" :progress="progress" @remove="removeFile" + @reject="rejectFile" + :openFileDialogOnClick="true" accept="image/*" list-type="picture" method="post" @@ -52,7 +54,6 @@ import i18n from "@/locales"; import {NSFWJS} from "nsfwjs"; import {animePredictImagePro} from "@/utils/tfjs/anime_classifier_pro.ts"; -import {fnDetectFace} from "@/utils/tfjs/face_extraction.ts"; import {cocoSsdPredict} from "@/utils/tfjs/mobilenet.ts"; import {predictLandscape} from "@/utils/tfjs/landscape_recognition.ts"; import {useRequest} from 'alova/client'; @@ -92,8 +93,9 @@ const progress: UploadProps['progress'] = { /** * 图片上传前的校验 * @param file + * @param fileList */ -async function beforeUpload(file: File) { +async function beforeUpload(file: File, fileList: File[]) { predicting.value = true; upload.clearPredictResult(); progressPercent.value = 0; // 初始化进度条 @@ -132,16 +134,18 @@ async function beforeUpload(file: File) { if (isNSFW) { message.error(i18n.global.t('comment.illegalImage')); - predicting.value = false; + progressPercent.value = 100; // 重置进度条 progressStatus.value = 'exception'; // 异常状态 + fileList.pop(); // 清空文件列表 + predicting.value = false; return false; } // 提取 EXIF 数据 const exifData = await extractAllExifData(file); if (exifData) { - upload.exifData = exifData; + upload.predictResult.exif = exifData; } // 判断是否为截图 @@ -158,14 +162,7 @@ async function beforeUpload(file: File) { progressPercent.value = 100; // 直接完成 return true; } - // 人脸检测 - const faceImageData = await fnDetectFace(image); - if (faceImageData) { - upload.predictResult.hasFace = true; - predicting.value = false; - progressPercent.value = 100; // 直接完成 - return true; - } + //目标检测和风景检测并行处理 const [cocoResults, landscape] = await Promise.all([ cocoSsdPredict(image), // 目标检测 @@ -218,7 +215,6 @@ async function customUploadRequest(file: any) { formData.append("data", JSON.stringify({ fileType: file.file.type, ...upload.predictResult, - exif: JSON.stringify(upload.exifData) || '', })); watch( () => uploading.value, @@ -248,6 +244,14 @@ function cancelUpload() { progressPercent.value = 0; // 重置进度条 } +/** + * 拒绝文件回调 + * @param fileList + */ +function rejectFile(fileList: any) { + fileList.value.pop(); +} + /** * 删除文件 * @param file