当前位置: 首页 > news >正文

解决由Tomcat部署前端改成nginx部署,导致大写.JPG结尾文件无法访问问题

前言:因信创替代要求,在麒麟服务器部署新的应用。

原先的架构:前端tomcat部署,源码部署java应用(ps:前后端,文件都在同一台服务器上),前端访问后端,再通过后端应用访问图片/视频/报纸等资源;

新的架构:采用了nginx部署前端(A服务器),docker部署后端(B服务器,文件也在该服务器上),访问nginx,然后通过nginx转发到后端,再通过后端应用访问图片/视频/报纸等资源。

现象:大写的.JPG结尾的图片无法访问到

按F12看到的该图片返回的格式是text/plain,打印日志发现,java代码返回的图片格式是null。

验证:我部署了一个nginx,通过nginx访问图片是可以访问的,不管是大写结尾还是小写结尾(证明图片本身没有问题);上传了一张照片以.jpg是访问的,手动改成了.JPG无法访问😌(说明是文件格式问题)。

核心问题:Java MIME映射默认不识别大写扩展名

当通过 Java 代码读取图片流(而非直接操作文件路径)时,大写.JPG扩展名无法识别contentType,核心原因是:流操作场景下,Files.probeContentType(Path)等依赖扩展名的方法失效,且纯流解析默认不关联扩展名大小写

解决方案

1. 已知扩展名(从路径 / 文件名获取)→ 手动映射

若能从图片资源路径中提取扩展名(如/path/image.JPG中的JPG),可先将扩展名转为小写,再通过 “预定义映射表” 或 “自定义逻辑” 匹配contentType,避免依赖系统配置。

import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;public class StreamContentTypeResolver {// 预定义常见图片扩展名→ContentType映射(不区分大小写,统一小写匹配)private static final Map<String, String> EXTENSION_CONTENT_TYPE_MAP = new HashMap<>();static {// 初始化映射表(可扩展其他格式)EXTENSION_CONTENT_TYPE_MAP.put("jpg", "image/jpeg");EXTENSION_CONTENT_TYPE_MAP.put("jpeg", "image/jpeg");EXTENSION_CONTENT_TYPE_MAP.put("png", "image/png");EXTENSION_CONTENT_TYPE_MAP.put("gif", "image/gif");EXTENSION_CONTENT_TYPE_MAP.put("bmp", "image/bmp");}/*** 结合“资源路径”和“流”解析ContentType(优先用扩展名,兜底用流签名)* @param resourcePath 图片资源路径(如"/path/image.JPG")* @param inputStream 图片流(需支持mark/reset,避免解析后流不可用)* @return ContentType(如"image/jpeg")*/public static String resolveContentType(String resourcePath, InputStream inputStream) {try {// 1. 从路径提取扩展名,转为小写Path path = Paths.get(resourcePath);String fileName = path.getFileName().toString().toLowerCase();String extension = null;int lastDotIndex = fileName.lastIndexOf('.');if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {extension = fileName.substring(lastDotIndex + 1);}// 2. 用预定义的映射表匹配ContentType(处理大写扩展名)if (extension != null && EXTENSION_CONTENT_TYPE_MAP.containsKey(extension)) {return EXTENSION_CONTENT_TYPE_MAP.get(extension);}// 3. 兜底:若扩展名匹配失败,用流签名解析(见方案2)return resolveContentTypeByStreamSignature(inputStream);} catch (Exception e) {e.printStackTrace();return null;}}public static void main(String[] args) throws Exception {// 示例:模拟大写JPG路径和流String resourcePath = "/data/images/test.JPG"; // 大写扩展名InputStream inputStream = StreamContentTypeResolver.class.getResourceAsStream("/test.JPG"); // 从类路径读取流String contentType = resolveContentType(resourcePath, inputStream);System.out.println("解析的ContentType:" + contentType); // 输出 "image/jpeg"}
}

2. 未知扩展名(仅能操作流)→ 读取文件签名

若无法获取扩展名(如流来自网络下载、数据库 BLOB),需直接读取流的前几个字节文件签名 判断类型 ——JPEG 的签名是FF D8 FF与扩展名大小写无关,是最可靠的方式。

关键注意点:
  • 流必须支持mark/reset(如BufferedInputStream),避免解析后流被 “读空”,影响后续业务使用
  • 读取签名后需reset()流,恢复到初始位置。
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;public class StreamContentTypeResolver {// 续上方案1的类,添加流签名解析方法/*** 通过流的文件签名解析ContentType(不依赖扩展名)* @param inputStream 图片流(需包装为BufferedInputStream以支持mark/reset)* @return ContentType(如"image/jpeg")*/public static String resolveContentTypeByStreamSignature(InputStream inputStream) throws IOException {// 包装为BufferedInputStream,确保支持mark/reset(若原流已支持可跳过)BufferedInputStream bis = new BufferedInputStream(inputStream);bis.mark(10); // 标记前10字节(足够读取所有图片格式的签名)try {// 读取前3字节(多数图片格式的签名长度≤3)byte[] signature = new byte[3];int readLen = bis.read(signature);if (readLen < 3) {return null; // 流过短,无法识别}// 匹配文件签名(按优先级排序)// JPEG签名:FF D8 FFif (signature[0] == (byte) 0xFF && signature[1] == (byte) 0xD8 && signature[2] == (byte) 0xFF) {return "image/jpeg";}// PNG签名:89 50 4Eif (signature[0] == (byte) 0x89 && signature[1] == (byte) 0x50 && signature[2] == (byte) 0x4E) {return "image/png";}// GIF签名:47 49 46if (signature[0] == (byte) 0x47 && signature[1] == (byte) 0x49 && signature[2] == (byte) 0x46) {return "image/gif";}// 其他格式可继续扩展(如BMP:42 4D 36)return null;} finally {// 重置流到初始位置,供后续业务使用(关键!)bis.reset();}}public static void main(String[] args) throws Exception {// 示例:仅通过流解析大写JPG(未知扩展名)InputStream rawStream = StreamContentTypeResolver.class.getResourceAsStream("/test.JPG");InputStream bufferedStream = new BufferedInputStream(rawStream); // 包装为支持mark的流String contentType = resolveContentTypeByStreamSignature(bufferedStream);System.out.println("流签名解析的ContentType:" + contentType); // 输出 "image/jpeg"// 后续可继续使用bufferedStream(已reset,位置正确)// ... 业务逻辑(如保存流、处理图片)...}
}

3.统一扩展名大小写(全新系统推荐)

在Java代码中,先将文件路径/文件名的扩展名转为小写,再匹配MIME类型,从源头避免大小写问题。(但是对已上线的问题不友好)

4.手动添加大写扩展名的MIME映射

若无法修改文件名,可在代码中手动注册.JPG对应的MIME类型,强制让java识别

5.使用Files.probeContentType()并处理大小写(我采用下面这个方式解决的

思考:为什么tomcat部署就没问题

HTTP服务器通过“文件后缀 + MIME配置表” 匹配Content-Type。Tomcat可能默认包含了大小写后缀的MIME配置。Nginx默认不区分文件后缀大小写,且未为大写.JPG配置对应的MIME类型映射

给Nginx添加大写.JPG的MIME映射,我也试了这种方式,修改nginx的MIME配置表(sed -i 's/jpeg jpg/jpeg jpg JPG JPEG/g' /etc/nginx/mime.types),但是未解决问题,可能某个地方配置问题,空了还是想试下这个方法。

http://www.dtcms.com/a/363739.html

相关文章:

  • 火语言 RPA 界面应用生成:轻量化开发核心优势
  • 51单片机(单片机基础,LED,数码管)
  • 电脑配置不足怎么办,告别硬件束缚,川翔云电脑
  • kaggle中的2D目标检测训练trick总结
  • OCR 识别准确率的关键影响因素
  • 【嵌入式电机控制#进阶7】V/F强拖启动
  • Windows 11系统终极优化指南
  • 亚马逊的领导力原则
  • UCIE Specification详解(十四)
  • 【LeetCode 热题 100】1143. 最长公共子序列——(解法二)递推
  • 快速入门Vue3——基础语法
  • Linux文本处理工具
  • 梯度波导_FDTD_学习_代码
  • ubuntu之坑(十九)——VMware虚拟机扩容磁盘
  • git工具笔记
  • 若想将gpu的代码在昇腾npu上运行,创建docker应该创建怎么样的docker?(待完善)
  • C/C++哆啦A梦
  • Java 技术支撑 AI 系统落地:从模型部署到安全合规的企业级解决方案(二)
  • 【面试场景题】外卖平台如何扛住高峰期上千qps订单查询流量
  • Python错误调试测试——调试
  • GNU Make | C/C++项目自动构建入门
  • 【日常学习8】2025-9-3 学习控件Day2
  • 解决HyperMesh许可证与版本不匹配问题
  • 【107】基于51单片机智能炒菜机【Proteus仿真+Keil程序+报告+原理图】
  • Vue + fetchEventSource 使用 AbortController 遇到的“只能中止一次”问题解析与解决方案
  • LeetCode 844.比较含退格的字符串
  • Spring 事务原理解析:AOP 的一次完美落地
  • 高校党建信息管理系统的设计与实现-(源码+LW+可部署)
  • wpf模板之DataTemplate
  • HTML第五课:求职登记表