Failed to parse multipart servlet request; nested exception is java.lang.RuntimeException: java.nio.

当SpringBoot应用在Linux环境中遇到上传文件报错,原因可能是系统自动清理了临时目录。为了解决这个问题,可以自定义临时文件路径,避免因系统清理导致的NoSuchFileException。在项目配置中添加自定义临时目录,并在启动时检查并创建该目录,确保文件上传的正常进行。通过这种方法,可以确保服务稳定运行,不再受制于系统默认的临时文件清理机制。

生产问题记录日志:上传文件报错

Failed to parse multipart servlet request; nested exception is java.lang.RuntimeException: java.nio.file.NoSuchFileException: /data/tmp/tomcat_upload/undertow4345140605170222984upload

 1.造成原因分析:

Failed to parse multipart servlet request; nested exception is java.lang.RuntimeException: java.nio.file.NoSuchFileException: /data/tmp/tomcat_upload/undertow4345140605170222984upload

上传文件的临时路径找不到

2 原因说明
在linux系统中,springboot应用服务再启动(java -jar 命令启动服务)的时候,会在操作系统的/tmp目录下生成一个tomcat*的文件目录,上传的文件先要转换成临时文件保存在这个文件夹下面。

由于临时/tmp目录下的文件,在长时间(10天)没有使用的情况下,就会被系统机制自动删除掉。

 3 解决方法 自定义配置临时文件路径
在你的项目配置文件中添加自定义配置如下:

项目是微服务,注册中心是nacos,在nacos生成配置文件加一行配置即可

  

 同时需要在服务器上创建对应文件夹保证项目再次上传文件能找到对应文件。

发布nacos,重启服务即可,正常使用!

 扩展:

       这样需要你手动在你服务器根目录下创建这个文件夹,我们可以在项目启动的时候检查一下临时文件夹是否存在,不存在就创建

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.MultipartConfigElement;
import java.io.File;

@Configuration
public class MultipartConfig {
    @Value("${location.tempDir:/tmp/tomcat_upload}")
    private String tempDir;

    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        File tmpDirFile = new File(tempDir);
        // 判断文件夹是否存在
        if (!tmpDirFile.exists()) {
            //创建文件夹
            tmpDirFile.mkdirs();
        }
        factory.setLocation(tempDir);
        return factory.createMultipartConfig();
    }
}

是我@PostConstruct public void initOcrEngine() { tesseract = new Tesseract(); try { //语言包路径和支持语言 tesseract.setDatapath(“D:\maven_use\lingxi-lhc\lingxi-ai-extend\lingxi-ai-comparison\src\main\resources\tessdata”); tesseract.setLanguage(“eng+chi_sim”); tesseract.setPageSegMode(6); // 自动页面分割 tesseract.setOcrEngineMode(1); // LSTM引擎 } catch (Exception e) { throw new RuntimeException(“OCR引擎初始化失败: " + e.getMessage(), e); } } /** * 支持PDF和图片 / public String extractContent(MultipartFile file) { String contentType = file.getContentType(); String fileName = file.getOriginalFilename().toLowerCase(); if (contentType == null) { return “不支持的文件类型: " + contentType; } if (fileName.endsWith(”.pdf")) { return readPdfText(file); } return extractImageText(file); } /* * 读取PDF文本内容 * * @param file * @return / public String readPdfText(MultipartFile file) { try (PDDocument doc = PDDocument.load(file.getInputStream())) { PDFTextStripper stripper = new PDFTextStripper(); // 设置行分隔符 stripper.setLineSeparator(“\n”); // 设置字符间距 stripper.setSortByPosition(true); String rawText = stripper.getText(doc); System.out.println(“内容” + rawText); return rawText.trim(); } catch (Exception e) { return MessageUtils.message(“file.red.pdf.error”); } } /* * OCR识别图片内容 / private String extractImageText(MultipartFile file) { try { // 创建临时文件 Path tempFile = Files.createTempFile(“ocr_”, getFileExtension(file.getOriginalFilename())); Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING); // 执行OCR识别 File imageFile = tempFile.toFile(); String result = tesseract.doOCR(imageFile) .replaceAll(“\s+”, " ").trim(); System.out.println(“读取的内容” + result); // 清理临时文件 Files.deleteIfExists(tempFile); return result; } catch (Exception e) { return "OCR处理失败: " + e.getMessage(); } } private String getFileExtension(String filename) { if (filename == null) return “.tmp”; int dotIndex = filename.lastIndexOf(‘.’); return (dotIndex == -1) ? “.tmp” : filename.substring(dotIndex); } /* * 解析json / public JsonNode parseJson(String jsonContent) throws Exception { return this.objectMapper.readTree(jsonContent); } public List compareContent(String pdfText, JsonNode jsonConfig) { List results = new ArrayList<>(); // 去除读取内容中的多余空格 pdfText = pdfText.replaceAll(“\s+”, “”); // 处理JSON结构(支持单个对象或数组) JsonNode dataNode; if (jsonConfig.isArray() && jsonConfig.size() > 0) { dataNode = jsonConfig.get(0); } else if (jsonConfig.isObject()) { dataNode = jsonConfig; } else { results.add(new ValidationResult(“ERROR”, “JSON格式错误”, “期望一个对象或包含对象的数组”, “实际格式不匹配”, false)); return results; } // 动态定义地址字段列表 Set addressFields = new HashSet<>(); // 字段直接匹配 checkNonAddressFields(pdfText, dataNode, results, addressFields); // 连续匹配 checkAddressFields(pdfText, dataNode, results, addressFields); return results; } /* * 检查 JSON 中非地址字段是否严格存在于 PDF 文本中 / private void checkNonAddressFields(String pdfText, JsonNode jsonConfig, List results, Set addressFields) { Iterator<Map.Entry<String, JsonNode>> fields = jsonConfig.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); String fieldName = entry.getKey(); JsonNode valueNode = entry.getValue(); if (valueNode.isValueNode() && !addressFields.contains(fieldName)) { //去除多余空格 String expectedValue = valueNode.asText().trim().replaceAll(“\s+”, “”); if (expectedValue.isEmpty()) continue; // 直接进行字符串匹配 boolean found = pdfText.contains(expectedValue); results.add(new ValidationResult( “FIELD”, fieldName, expectedValue, found ? “Found” : “Not Found”, found )); } } } /* * 检查 JSON 中地址字段是否严格存在于 PDF 文本中 */ private void checkAddressFields(String pdfText, JsonNode jsonConfig, List results, Set addressFields) { // HanLP分词 List terms = HanLP.segment(pdfText); List addressParts = new ArrayList<>(); for (Term term : terms) { String word = term.word; if (word.matches(”\d{5,7}“)) { addressParts.add(word); } else if (term.nature.toString().startsWith(“ns”)) { addressParts.add(word); } } // 遍历 JSON 配置中的地址字段 Iterator<Map.Entry<String, JsonNode>> fields = jsonConfig.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); String fieldName = entry.getKey(); JsonNode valueNode = entry.getValue(); if (valueNode.isValueNode() && addressFields.contains(fieldName)) { //去除多余空格 String expectedValue = valueNode.asText().trim().replaceAll(”\s+“, “”); if (expectedValue.isEmpty()) continue; boolean found = false; for (String part : addressParts) { if (part.equals(expectedValue)) { found = true; break; } } results.add(new ValidationResult( “FIELD”, fieldName, expectedValue, found ? “Found” : “Not Found”, found )); } } } public JsonNode parsePipeSeparatedDataToJson(String inputData) throws Exception { Map<String, String> dataMap = parsePipeSeparatedData(inputData); return objectMapper.valueToTree(dataMap); } //解析filE_CONTENT数据 public Map<String, String> parsePipeSeparatedData(String filE_CONTENT) { Map<String, String> dataMap = new HashMap<>(); String[] lines = filE_CONTENT.split(”\n"); if (lines.length >= 2) { String[] headers = lines[0].split(“\|”); String[] values = lines[1].split(“\|”); for (int i = 0; i < headers.length; i++) { dataMap.put(headers[i], values[i]); } } return dataMap; } // 判断是否是以 | 分隔的数据 public boolean isPipeSeparatedData(String inputData) { return inputData.contains(“|”); }上面的我的代码 我读取pdf文件内容 可以跟我传的json内容做比较和校验都是对的 然后我传这个数据"filE_CONTENT": "PALLET_ID|KNBOXNO|CARRIER|COC|CTRY|HAWB|PO|ORIGIN|INVOICENO|CARTONS|SHIPID|SHIP_DATE|TEL|SSCC|RETURN_TO1|RETURN_TO2|RETURN_TO3|RETURN_TO4|RETURN_TO5|RETURN_TO6|RETURN_TO7|RETURN_TO8|SHIP_TO1|SHIP_TO2|SHIP_TO3|SHIP_TO4|SHIP_TO5|SHIP_TO6|SHIP_TO7|SHIP_TO8|LINEITEM1|MPN1|QTY1|LINEITEM2|MPN2|QTY2|LINEITEM3|MPN3|QTY3|\nFO2501000233P0002||DGF-AD|NL|NL|8VG8286|0638138589|PVG|8VG8286|61/84|73292885370002|06/01/2025|00000000|001959499064098506|ADI EMEIA Logistics|Inbound Department|||||||PEGATRON CORPORATION|c/o DP World Logistics Netherlands BV|Van Hilststraat 23|5145 RK Waalwijk,Netherlands|5145 RK Waalwijk Netherlands||||00010|MLPF3AA/A|10|"它的\n 后的是要打印的内容 | 是来区分不同的参数读取pdf文件内容 比较和返回结果都是错误的 @PostMapping(value = “/compare”, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }) public R compare(@RequestPart(“File”) MultipartFile file, @RequestPart(“jsonContent”) String jsonContent) { // 读取 PDF 文本 String pdfText = compareService.extractContent(file); // 解析 JSON 配置 JsonNode jsonConfig = null; try { if (compareService.isPipeSeparatedData(jsonContent)) { jsonConfig = compareService.parsePipeSeparatedDataToJson(jsonContent); System.out.println(“数据”+jsonContent); } else { jsonConfig = compareService.parseJson(jsonContent); } } catch (Exception e) { return R.fail(MessageUtils.message(“failed.convert.json”)); } // 执行对比校验 List results = compareService.compareContent(pdfText, jsonConfig); // 返回没有匹配成功的数据 List failedResults = new ArrayList<>(); for (ValidationResult result : results) { if (!result.isValid()) { failedResults.add(result); } } return failedResults.isEmpty() ? R.ok(“条件符合规范”) : R.ok(failedResults); }这个是我的接口 这个整体修改后的代码 不管是json还是 我传的都是可以比较和校验数据
07-12
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值