getClass().getResource("/").getFile().toString().substring(1)在window和linux下的不同。

本文探讨了一个项目从Windows移植到Linux环境下遇到的资源路径不一致的问题,详细分析了getClass().getResource(/).getFile()在不同操作系统下返回路径的区别,并提供了解决方案。

背景:项目在window下运行正常,在linux下运行不正常,tomcat还没报错记录(好奇怪。。。)

原因:

1.getClass().getResource("/").getFile().toString().substring(1)获取资源路径在window和linux下不一样。

String path2 = getClass().getResource("/").getFile().toString().substring(1);
String fileNameCtx = path2 + "/META-INF/spring/applicationContext.xml";
LOGGER.debug("Start produce applicationContext.xml,path is "+fileNameCtx);

window下是正确的;

路径:E:/develop/Java/apache-tomcat-7.0.70/webapps/innovate/WEB-INF/classes//META-INF/spring/applicationContext.xml

linux下面不正确


路径是:usr/local/src/apache-tomcat-7.0.70/webapps/innovate/WEB-INF/classes//META-INF/spring/applicationContext.xml

注意:前面缺少了/,这个路径在linux下是不存在的(查了好长时间,泪奔。。)

而异常时通过e.printStackTrace()打印的,实际就是system.err,日志文件得用LOGGER.err()才能写到日志文件中。

经过测试这样的实现是可行的,我给你一份错误的代码,你把这个实现进去 package com.example.server.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.example.base.config.KafkaService; import com.example.base.config.RedisCache; import com.example.base.config.WechatConfigService; import com.example.base.util.HttpClientUtils; import com.example.page.manage.domain.Ziliao; import com.example.page.manage.service.impl.ZiliaoServiceImpl; import com.example.server.dto.WeChatDTO; import com.example.utils.core.domain.AjaxResult; import com.example.utils.utils.StringUtils; import io.micrometer.core.annotation.Timed; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.multipart.MultipartFile; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.TimeUnit; import static com.alibaba.fastjson.JSON.parseObject; @Service public class WechatService { // 微信配置 @Value("${wechat.appid}") private String appid; @Value("${wechat.secret}") private String secret; // Redis缓存 @Autowired private RedisCache redisCache; @Autowired private WechatConfigService wechatConfigService; // 注入配置服务 @Autowired private KafkaService kafkaService; // 文件存储路径 @Value("${file.upload-dir}") private String uploadDir; // 如:/data/wechat_files @Autowired private ZiliaoServiceImpl ziliaoService; // 微信Token缓存配置 private static final Logger log = LoggerFactory.getLogger(WechatService.class); private static final String WECHAT_TOKEN_KEY = "wechat:access_token"; private static final long TOKEN_EXPIRE = 7200; // 2小时(微信Token有效期) private HttpClientUtils httpClientUtils; /** * 获取微信jsapi_ticket(带Redis缓存) */ public AjaxResult getWechatConfig(Map<String, String> req) { try { String url = req.get("url"); if (StringUtils.isBlank(url)) { throw new IllegalArgumentException("URL参数不能为空"); } // 1. 生成nonceStrtimestamp String nonceStr = UUID.randomUUID().toString().replace("-", "").substring(0, 16); long timestamp = System.currentTimeMillis() / 1000; // 2. 获取jsapi_ticket(带缓存) String jsapiTicket = getJsapiTicketWithCache(); // 3. 计算签名(确保参数顺序拼接正确) String signature = calculateSignature(jsapiTicket, nonceStr, timestamp, url); AjaxResult tokenAjax = getToken(); String token = (String) tokenAjax.get("msg"); // 4. 返回配置参数 return AjaxResult.success(Map.of("appId", appid, // 替换为实际AppIDs "timestamp", String.valueOf(timestamp), "nonceStr", nonceStr, "signature", signature, "token", token)); } catch (Exception e) { System.err.println("获取微信配置失败:" + e.getMessage()); e.printStackTrace(); // 打印完整堆栈 return AjaxResult.error("获取微信配置异常"); } } // 生成随机字符串(16位) private String generateNonceStr() { return UUID.randomUUID().toString().replace("-", "").substring(0, 16); } // 获取jsapi_ticket(带Redis缓存) public String getJsapiTicketWithCache() throws IOException { String cacheKey = "wechat:jsapi_ticket"; String ticket = redisCache.getCacheObject(cacheKey); if (ticket == null) { synchronized (this) { ticket = redisCache.getCacheObject(cacheKey); if (ticket == null) { // 获取access_token String accessToken = getAccessToken(); // 调用微信API获取jsapi_ticket String url = String.format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=%s", accessToken); String response = httpClientUtils.get(url); JSONObject json = parseObject(response); if (json.getIntValue("errcode") != 0) { throw new RuntimeException("获取jsapi_ticket失败: " + json.getString("errmsg")); } ticket = json.getString("ticket"); redisCache.setCacheObject(cacheKey, ticket, 7200, TimeUnit.SECONDS); // 缓存2小时 } } } return ticket; } // 计算HMAC-SHA1签名 public String calculateSignature(String jsapiTicket, String nonceStr, long timestamp, String url) { String rawString = String.format("jsapi_ticket=%s&noncestr=%s×tamp=%d&url=%s", jsapiTicket, nonceStr, timestamp, url); try { Mac mac = Mac.getInstance("HmacSHA1"); SecretKeySpec secretKeySpec = new SecretKeySpec(jsapiTicket.getBytes(StandardCharsets.UTF_8), "HmacSHA1"); mac.init(secretKeySpec); byte[] hash = mac.doFinal(rawString.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(hash); } catch (Exception e) { throw new RuntimeException("签名计算失败", e); } } /** * 获取微信Access Token(带Redis缓存) */ public AjaxResult getToken() throws IOException { String token = redisCache.getCacheObject(WECHAT_TOKEN_KEY); token = "95_XrWtIb9U2oqStOC4pITcyhW_W328kuSADUtctYS4q4fyYhB0JV481qtMntxNJVmjCL_CN4ovVsnaaQvF6R5RhCTs87QIbPoVIVIzPhSiV7KxwPLN83q3PVVIhWcANTiAJAVIO"; if (token == null) { synchronized (this) { // 双重检查锁防止缓存击穿 token = redisCache.getCacheObject(WECHAT_TOKEN_KEY); if (token == null) { token = fetchWeChatTokenFromApi(); // 调用微信API获取 redisCache.setCacheObject(WECHAT_TOKEN_KEY, token, (int) TOKEN_EXPIRE, TimeUnit.SECONDS); } } } return AjaxResult.success(token); } /** * 微信登录(获取openid) */ public String login(String code) { try { String url = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.connect(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); return response.toString(); } catch (Exception e) { e.printStackTrace(); return "登录失败: " + e.getMessage(); } } /** * 使用kafka消费从微信服务器下载指定 media_id 的图片,并根据 type 保存到大图或小图目录 */ public AjaxResult downloadFromWeChatByKafka(ArrayList<WeChatDTO> weChatDTOList) { Map<String, String> result = new HashMap<>(); // 0.时间戳-文件名 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String formattedTimestamp = sdf.format(new Date()); try { for (WeChatDTO weChatDTO : weChatDTOList) { // 1. 参数基础校验 String type = weChatDTO.getType(); if (type == null || (!type.equals("temp") && !type.equals("stemp"))) { return AjaxResult.error("参数 type 错误,必须是 'temp' 或 'stemp'"); } String mediaId = weChatDTO.getMedia_id(); if (mediaId == null || mediaId.trim().isEmpty()) { return AjaxResult.error("参数 media_id 不能为空"); } // 2.构造下载任务对象 WeChatDTO task = new WeChatDTO(); task.setMedia_id(mediaId); task.setType(type); task.setBianhao(UUID.randomUUID().toString()); // uuid+文件名(当前时间戳) // 时间戳-文件名 task.setFilename(formattedTimestamp); // 3. 发送任务到 Kafka kafkaService.sendMessage("file-download-task", JSONObject.toJSONString(task)); // 4. 返回kafka的请求id-》ziliao入库id-》带时间戳(用于命名文件) result.put("bianhao", task.getBianhao()); result.put("filename", task.getFilename()); System.out.println("已提交下载任务到 Kafka,mediaId = " + mediaId + ", type = " + type); } // 4. 立即返回成功响应(实际下载由后台异步执行) // return AjaxResult.success("文件下载任务已提交,后台正在处理"); return AjaxResult.success(result); } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("提交下载任务失败:" + e.getMessage()); } } /** * 私有方法:从微信API获取Access Token(经过缓存) */ private String fetchWeChatTokenFromApi() throws IOException { String url = "https://api.weixin.qq.com/cgi-bin/token?" + "grant_type=client_credential&appid=" + appid + "&secret=" + secret; HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestMethod("GET"); conn.connect(); if (conn.getResponseCode() != 200) { throw new IOException("微信Token接口返回错误状态码: " + conn.getResponseCode()); } String response = new String(conn.getInputStream().readAllBytes(), StandardCharsets.UTF_8); JSONObject json = JSONObject.parseObject(response); if (!json.containsKey("access_token")) { throw new IOException("微信Token获取失败: " + json.getString("errmsg")); } // 缓存Token(有效期7200秒) redisCache.setCacheObject(WECHAT_TOKEN_KEY, json.getString("access_token"), (int) TOKEN_EXPIRE, TimeUnit.SECONDS); return json.getString("access_token"); } /** * 私有方法:获取Access Token(带缓存逻辑) */ public String getAccessToken() throws IOException { String token = redisCache.getCacheObject(WECHAT_TOKEN_KEY); token = "95_XrWtIb9U2oqStOC4pITcyhW_W328kuSADUtctYS4q4fyYhB0JV481qtMntxNJVmjCL_CN4ovVsnaaQvF6R5RhCTs87QIbPoVIVIzPhSiV7KxwPLN83q3PVVIhWcANTiAJAVIO"; if (token == null) { token = fetchWeChatTokenFromApi(); redisCache.setCacheObject(WECHAT_TOKEN_KEY, token, (int) TOKEN_EXPIRE, TimeUnit.SECONDS); } return token; } /** * 图片查看接口(通用,支持原图/缩略图)前端调用 */ public ResponseEntity<?> downloadFile(String fileName, String type) { try { if (fileName == null || fileName.trim().isEmpty()) { return ResponseEntity.badRequest().body("文件名不能为空"); } if (!"temp".equals(type) && !"stemp".equals(type)) { return ResponseEntity.badRequest().body("类型错误(仅支持temp/stemp)"); } String subDir = "temp".equals(type) ? "temp" : "stemp"; String dirPath = uploadDir + "/upload/" + subDir + "/" + fileName; /* 2. 路径规范化 去掉 file:/ 前缀 */ if (dirPath.startsWith("file:/")) { dirPath = dirPath.substring("file:/".length()); } Path filePath = Paths.get(dirPath).toAbsolutePath().normalize(); if (!filePath.startsWith(dirPath) || !Files.exists(filePath)) { System.out.println("code:404, 文件下载失败,目录不存在: " + filePath); return ResponseEntity.notFound().build(); } // 4. 加载资源并设置响应头 Resource resource = new UrlResource(filePath.toUri()); String contentType = Files.probeContentType(filePath); // 自动检测文件类型(如image/jpeg) if (contentType == null) { contentType = "application/octet-stream"; // 默认类型(避免浏览器无法识别) } return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType)) // 动态设置内容类型 .body(resource); } catch (Exception e) { return ResponseEntity.internalServerError().body("下载失败: " + e.getMessage()); } } /** * 根据文件获取图片的实际格式 */ private String getImageFormat(File imageFile) throws IOException { String format = "jpg"; // 默认假设为 jpg String fileName = imageFile.getName().toLowerCase(); if (fileName.endsWith(".png")) { format = "png"; } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { format = "jpg"; } else if (fileName.endsWith(".gif")) { format = "gif"; } else if (fileName.endsWith(".bmp")) { format = "bmp"; } else { // 如果无法通过扩展名判断,尝试通过文件头判断 format = getImageFormatByHeader(imageFile); } return format; } /** * 通过文件头判断图片的实际格式 */ private String getImageFormatByHeader(File imageFile) throws IOException { try (InputStream is = new FileInputStream(imageFile)) { byte[] header = new byte[8]; int bytesRead = is.read(header); if (bytesRead < 8) { return "unknown"; } // JPEG if (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8 && header[2] == (byte) 0xFF) { return "jpg"; } // PNG if (header[0] == (byte) 0x89 && header[1] == (byte) 0x50 && header[2] == (byte) 0x4E && header[3] == (byte) 0x47 && header[4] == (byte) 0x0D && header[5] == (byte) 0x0A && header[6] == (byte) 0x1A && header[7] == (byte) 0x0A) { return "png"; } // GIF if ((header[0] == (byte) 0x47 && header[1] == (byte) 0x49 && header[2] == (byte) 0x46 && header[3] == (byte) 0x38) && (header[4] == (byte) 0x39 || header[4] == (byte) 0x37) && header[5] == (byte) 0x61) { return "gif"; } // BMP if (header[0] == (byte) 0x42 && header[1] == (byte) 0x4D) { return "bmp"; } return "unknown"; } } /** * 从微信服务器下载指定 media_id 的图片,并根据 type 保存到大图或小图目录 */ public AjaxResult downloadFromWeChat(@RequestBody ArrayList<WeChatDTO> weChatDTOList) { HttpURLConnection wechatConn = null; InputStream inputStream = null; Map<String, String> result = new HashMap<>(); // 生成16位时间戳(如20250731105217356)作为多个文件的唯一标识(文件名) SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); String formattedTimestamp = sdf.format(new Date()); // 生成时间戳用于文件命名(这段代码属于方案二) // String timestamp = String.valueOf(System.currentTimeMillis()); // String uuid = UUID.randomUUID().toString().replace("-", ""); for (WeChatDTO weChatDTO : weChatDTOList) { try { // ====================== // 1. 获取微信 Access Token // ====================== String accessToken = null; accessToken = getAccessToken(); if (accessToken == null || accessToken.isEmpty()) { return AjaxResult.error("获取微信 Access Token 失败"); } // 测试使用(必须与上传时使用的token一致),实际业务注释本行代码即可 // accessToken = "95_GnZ7oa-IUO5ZVxy6qlh9broUq4NxS7lD3TEcWotYlqX75nheaOVMQXqAeJJsRQe0fq-u1sEsXJwaMVRlTr5jcZfdJmP_6yvQw0XbB2_NHHC0wmHckEL1fYaPQtsOHDcAIATMI"; // ====================== // 2. 检查 type 参数 // ====================== String type = weChatDTO.getType(); if (type == null || (!type.equals("temp") && !type.equals("stemp"))) { return AjaxResult.error("参数 type 错误,必须是 'temp' 或 'stemp'"); } // ====================== // 3. 调用微信 API 下载图片 // ====================== String wechatDownloadUrl = "https://api.weixin.qq.com/cgi-bin/media/get" + "?access_token=" + accessToken + "&media_id=" + weChatDTO.getMedia_id(); URL url = new URL(wechatDownloadUrl); wechatConn = (HttpURLConnection) url.openConnection(); wechatConn.setRequestMethod("GET"); wechatConn.connect(); int responseCode = wechatConn.getResponseCode(); if (responseCode != 200) { // 读取微信返回的错误信息 InputStream errorStream = wechatConn.getErrorStream(); String errorMsg = errorStream != null ? new String(errorStream.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8) : "未知错误,HTTP状态码: " + responseCode; return AjaxResult.error("微信下载失败: " + errorMsg); } // ====================== // 4. 定义存储路径 // ====================== String contentType = wechatConn.getContentType(); System.out.println("Content-Type: " + contentType); String fileExtension = ".jpg"; // 默认扩展名 if (contentType != null) { if (contentType.toLowerCase().contains("image/jpeg")) { fileExtension = ".jpg"; } else if (contentType.toLowerCase().contains("image/png")) { fileExtension = ".png"; } else if (contentType.toLowerCase().contains("image/gif")) { fileExtension = ".gif"; } else { System.out.println("未识别的 Content-Type: " + contentType + ",默认使用 .jpg"); } } // 根据 type 动态设置保存目录 // 大图:temp,小图:stemp String subDir = type.equals("temp") ? "temp" : "stemp"; String dirPath = uploadDir + "/upload/" + subDir; /* 2. 路径规范化 去掉 file:/ 前缀 */ if (dirPath.startsWith("file:/")) { dirPath = dirPath.substring("file:/".length()); } Path saveDirPath = Paths.get(dirPath).toAbsolutePath().normalize(); // 打印路径(调试用) System.out.println(type + " 图片存储目录: " + saveDirPath); // 创建目录(如果不存在) try { Files.createDirectories(saveDirPath); } catch (IOException e) { return AjaxResult.error("创建目录失败: " + e.getMessage()); } // ====================== // 5. 生成唯一文件名 // ====================== // 方案一 String fileName = formattedTimestamp + fileExtension; // 方案二 // String fileName = timestamp + "_" + uuid + fileExtension; // 1754703957349_b03eb7428f994c73b7f7282ea1bfda8c.jpg // ====================== // 6. 保存图片到对应目录 // ====================== Path filePath = saveDirPath.resolve(fileName); File imageFile = filePath.toFile(); try (InputStream in = wechatConn.getInputStream(); // 微信返回的图片二进制流 OutputStream out = new FileOutputStream(imageFile) // 保存到本地文件 ) { byte[] buffer = new byte[8192]; // 8KB 缓冲区 int bytesRead; long totalBytesRead = 0; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); totalBytesRead += bytesRead; } System.out.println("图片下载完成,总字节数: " + totalBytesRead); } catch (IOException e) { e.printStackTrace(); return AjaxResult.error("保存图片文件失败: " + e.getMessage()); } // 检查文件是否成功保存 if (!imageFile.exists()) { return AjaxResult.error(type + " 图片文件保存失败,路径: " + filePath); } // 打印文件大小,用于调试 long fileSize = imageFile.length(); System.out.println(type + " 图片文件大小: " + fileSize + " 字节"); // ====================== // 7. 返回访问 URL // ====================== // 假设你的 Spring Boot 静态资源映射了 /upload/** 到 upload 目录 // 比如通过 WebConfig 添加了资源映射 if ("temp".equals(type)) { String fileUrl = "/upload/" + subDir + "/" + fileName; // 数据入库 Ziliao ziliao = new Ziliao(); // 图片路径 ziliao.setTupian(subDir + "/" + fileName); // 图片扩展名 ziliao.setKuozhanming(fileExtension); // 时间 ziliao.setShijian(new Date()); // 项目编号 ziliao.setBianhao(UUID.randomUUID().toString()); // 大小图标识 ziliao.setWendangtype(type); int code = ziliaoService.insertZiliao(ziliao); if (code == 0) { return AjaxResult.error(500, "图片信息入库失败"); } String fileUrl1 = result.put("fileUrl", fileUrl); result.put("bianhao", ziliao.getBianhao()); } } catch (Exception e) { e.printStackTrace(); return AjaxResult.error("下载失败: " + e.getMessage()); } finally { // 关闭资源 if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (wechatConn != null) { wechatConn.disconnect(); } } } return AjaxResult.success(result); } /** * 上传图片到微信临时素材接口 */ public AjaxResult uploadImage(MultipartFile file) { // 1. 基本校验 if (file == null || file.isEmpty()) { return AjaxResult.error("请选择要上传的图片文件"); } String originalFilename = file.getOriginalFilename(); if (originalFilename == null || !isValidImageExtension(originalFilename)) { return AjaxResult.error("不支持的图片格式(仅允许 jpg、jpeg、png、gif)"); } // 微信限制:图片最大 2MB if (file.getSize() > 2 * 1024 * 1024) { return AjaxResult.error("微信限制图片大小不能超过 2MB"); } // 2. 获取 AccessToken String accessToken; try { accessToken = getAccessToken(); } catch (IOException e) { log.error("[uploadImage] 获取微信 AccessToken 失败", e); return AjaxResult.error("系统异常,无法获取微信令牌"); } // 3. 构建上传地址 String uploadUrl = String.format("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=image", accessToken); CloseableHttpClient httpClient = null; CloseableHttpResponse response = null; try { httpClient = HttpClients.custom().setConnectionTimeToLive(5, TimeUnit.SECONDS).build(); HttpPost httpPost = new HttpPost(uploadUrl); MultipartEntityBuilder builder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE).setCharset(StandardCharsets.UTF_8).addBinaryBody("media", file.getInputStream(), ContentType.create(getMimeType(originalFilename)), originalFilename); httpPost.setEntity(builder.build()); // 4. 执行上传 response = httpClient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { String errorBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); log.error("[uploadImage] 微信返回非200状态码:{}, 响应体:{}", statusCode, errorBody); return AjaxResult.error("微信服务器异常:" + statusCode); } // 5. 解析响应 String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); JSONObject json = JSON.parseObject(responseBody); if (json.getIntValue("errcode") != 0) { String errMsg = json.getString("errmsg"); log.error("[uploadImage] 微信上传失败:{}", errMsg); return AjaxResult.error("微信上传失败:" + errMsg); } // 6. 异步触发下载任务 String mediaId = json.getString("media_id"); WeChatDTO dto = new WeChatDTO(); dto.setMedia_id(mediaId); dto.setBianhao(UUID.randomUUID().toString()); dto.setFilename(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + "_" + originalFilename); kafkaService.sendMessage("file-download-task", JSON.toJSONString(dto)); return AjaxResult.success(Map.of("mediaId", mediaId, "type", json.getString("type"), "msg", "上传成功,后台正在异步下载图片")); } catch (IOException e) { log.error("[uploadImage] 文件上传失败", e); return AjaxResult.error("文件处理失败:" + e.getClass().getSimpleName() + ": " + e.getMessage()); } catch (Exception e) { log.error("[uploadImage] 未知异常", e); return AjaxResult.error("系统异常,请稍后重试"); } finally { // 7. 确保资源关闭 if (response != null) { try { response.close(); } catch (IOException e) { log.warn("[uploadImage] 关闭 response 异常", e); } } if (httpClient != null) { try { httpClient.close(); } catch (IOException e) { log.warn("[uploadImage] 关闭 httpClient 异常", e); } } } } /** * 校验图片扩展名(防伪造MIME类型) * @param filename 文件名 * @return 是否合法 */ private boolean isValidImageExtension(String filename) { if (filename == null) { return false; } String lowerFilename = filename.toLowerCase(); return lowerFilename.endsWith(".jpg") || lowerFilename.endsWith(".jpeg") || lowerFilename.endsWith(".png") || lowerFilename.endsWith(".gif"); } private String getMimeType(String filename) { if (filename == null) return "image/jpeg"; String lower = filename.toLowerCase(); if (lower.endsWith(".png")) return "image/png"; if (lower.endsWith(".gif")) return "image/gif"; return "image/jpeg"; // 默认 } } 若无必要不要改动uploadImage以外的内容
最新发布
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kenick

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值