{"record":{"id":"73e44f6bba6a73e3","repo":"elunez/eladmin","slug":"error-73e44f","errorCode":null,"errorMessage":"只能上传图片","messagePattern":"只能上传图片","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java","lineNumber":78,"sourceCode":"    public void exportFile(HttpServletResponse response, LocalStorageQueryCriteria criteria) throws IOException {\n        localStorageService.download(localStorageService.queryAll(criteria), response);\n    }\n\n    @PostMapping\n    @ApiOperation(\"上传文件\")\n    @PreAuthorize(\"@el.check('storage:add')\")\n    public ResponseEntity<Object> createFile(@RequestParam String name, @RequestParam(\"file\") MultipartFile file){\n        localStorageService.create(name, file);\n        return new ResponseEntity<>(HttpStatus.CREATED);\n    }\n\n    @ApiOperation(\"上传图片\")\n    @PostMapping(\"/pictures\")\n    public ResponseEntity<LocalStorage> uploadPicture(@RequestParam MultipartFile file){\n        // 判断文件是否为图片\n        String suffix = FileUtil.getExtensionName(file.getOriginalFilename());\n        if(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){\n            throw new BadRequestException(\"只能上传图片\");\n        }\n        LocalStorage localStorage = localStorageService.create(null, file);\n        return new ResponseEntity<>(localStorage, HttpStatus.OK);\n    }\n\n    @PutMapping\n    @Log(\"修改文件\")\n    @ApiOperation(\"修改文件\")\n    @PreAuthorize(\"@el.check('storage:edit')\")\n    public ResponseEntity<Object> updateFile(@Validated @RequestBody LocalStorage resources){\n        localStorageService.update(resources);\n        return new ResponseEntity<>(HttpStatus.NO_CONTENT);\n    }\n\n    @Log(\"删除文件\")\n    @DeleteMapping\n    @ApiOperation(\"多选删除\")\n    public ResponseEntity<Object> deleteFile(@RequestBody Long[] ids) {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-tools/src/main/java/me/zhengjie/rest/LocalStorageController.java#L60-L96","documentation":"Thrown by LocalStorageController.uploadPicture (POST /localStorage/pictures, no @PreAuthorize on this endpoint in the shown region) when the uploaded file's extension does not map to FileUtil.IMAGE type. The check is extension-based only: FileUtil.getExtensionName on the original filename, then FileUtil.getFileType(suffix) compared to the IMAGE constant.","triggerScenarios":"Uploading a file whose extension is in the document/other category set (e.g. .pdf, .zip, .mp4, .txt, or no extension) to POST /localStorage/pictures; or a filename with trailing spaces/uppercase variants if the extension matcher is case-sensitive (e.g. .JPG not recognized).","commonSituations":"Frontend file-picker not filtering to images; user renames a non-image to .jpg (passes the check — it only inspects the name, not content); HEIC/WebP or other newer formats missing from the extension list; uppercase extensions from Windows uploads.","solutions":["Upload a genuine image with a standard lowercase extension (jpg/jpeg/png/gif/bmp).","If a legitimate image type is rejected, extend FileUtil's image extension list to include it (and make the comparison case-insensitive).","Use the generic file upload endpoint (POST /localStorage with @PreAuthorize('storage:add')) for non-image files.","Optionally add content sniffing (e.g. checking magic bytes) if extension spoofing matters for your threat model."],"exampleFix":"// before\nString suffix = FileUtil.getExtensionName(file.getOriginalFilename());\nif(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){\n    throw new BadRequestException(\"只能上传图片\");\n}\n\n// after: case-insensitive suffix check\nString suffix = FileUtil.getExtensionName(file.getOriginalFilename());\nString type = StrUtil.isBlank(suffix) ? \"\" : FileUtil.getFileType(suffix.toLowerCase());\nif(!FileUtil.IMAGE.equals(type)){\n    throw new BadRequestException(\"只能上传图片\");\n}","handlingStrategy":"validation","validationCode":"// client-side pre-check mirroring the server rule\nconst IMAGE_EXT = ['jpg','jpeg','png','gif','bmp','ico'];\nconst ext = file.name.split('.').pop().toLowerCase();\nif (!IMAGE_EXT.includes(ext)) { showToast('只能上传图片'); return; }","typeGuard":null,"tryCatchPattern":"try { await uploadPicture(file); } catch (err) { if (err.response?.status === 400 && err.response.data.message === '只能上传图片') { /* prompt user to pick an image */ } }","preventionTips":["Set the file input accept=\"image/*\" so the picker filters for you.","Validate the extension in the frontend before the multipart POST to save a round trip.","Remember the server checks the NAME only — do not rely on it for content security."],"tags":["file-upload","validation","image","extension-check"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}