the request was rejected because no multipart boundary was found

1. 需求描述

想要在前端上传一个PDF 发票,经过后端解析PDF之后,将想要的值自动回填到对应的输入框中

2. 报错信息

org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
	at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:205) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:224) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.<init>(FileItemIteratorImpl.java:142) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:252) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:276) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.connector.Request.parseParts(Request.java:2932) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.connector.Request.getParts(Request.java:2834) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1098) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:95) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:88) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:122) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1209) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1043) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:696) ~[tomcat-embed-core-9.0.68.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:779) ~[tomcat-embed-core-9.0.68.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.68.jar:9.0.68]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_321]

3. 探索过程

1. 使用postman 排除后端错误

使用postman做接口测试,上传文件确实能够返回正确的值,可以确定是前端代码的问题
在这里插入图片描述
我的前端代码

<template>
  <div>
    <input type="file" @change="onFileChange" accept="application/pdf" />
    <button @click="uploadAndFillData" enctype="multipart/form-data">上传并回填数据</button>
    <!-- <vxe-button size="mini" status="primary" style="height:25px; margin: 0 0 2px 15px;" @click="uploadAndFillData">导入</vxe-button> -->
    <!-- 表单或区域来显示回填的数据 -->
    <div v-if="formData">
      <!-- 使用formData来显示或更新UI -->
      <!-- <p>数据: {{ formData.someKey }}</p>  -->
      <!-- 如果数据是只读的,可以这样显示 -->
      <div>
        <p><strong>发票号码:</strong> {{ formData.number }}</p>
        <p><strong>开票日期:</strong> {{ formData.date }}</p>
        <p><strong>总金额:</strong> {{ formData.amount }}</p>
        <p><strong>备注:</strong> {{ formData.remarks }}</p>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios';  
export default {
  data() {
    return {
      selectedFile: null,
      formData: null, // 用于存储从后端返回的数据
    };
  },
  methods: {
    onFileChange(e) {
      this.selectedFile = e.target.files[0];
    },

    uploadAndFillData() {
      // 检查文件是否已经选择
      if (!this.selectedFile) {
        alert("请先选择一个文件!");
        return;
      }

      const formData = new FormData(); // 创建一个FormData 对象,用于构建将要发送的数据
      formData.append("file", this.selectedFile); // 将文件添加到FormData中,字段名为file

      this.$http({
            url: `/api/invoice/upload`,
            method: 'post',
            data: formData,
            headers: {
              'Content-Type': 'multipart/form-data'
              // 'Content-Type': 'application/json'
            },
            // enctype:"multipart/form-data",
          })
      .then((response) => {
          this.formData = response.data; // 将解析后的数据保存到formData中
        }).catch((error) => {
          console.error("Error uploading file:", error);
          alert("上传文件时发生错误!");
        });
    },
  },
};
</script>

2. 搜索网上的解决方法

网上的方法很多,但是都没有能够解决我的问题

  1. 注释掉header中的content-type,直接404
  2. 使用axios也是 404
  3. 在application.yml 中添加文件大小的限制配置【500,还是报原错误】

3. 解决方法

求助于GPT检查前端页面的代码,GPT给出的说法如下,我确实是没有通过Vue插件方式配置axios,所以肯定不能使用this.$http , 顾改用axios的方式向后端发请求,但是之前说了会报错404,猜测可能是因为反向代理配置问题,导致前端并没有通过这个地址请求到后端,所以采用粗暴的方法,直接将后端地址写在axios的url中,并在controller上配置注解@CrossOrigin
在这里插入图片描述
修改后的前端代码

<template>
  <div>
    <input type="file" @change="onFileChange" accept="application/pdf" />
    <button @click="uploadAndFillData" enctype="multipart/form-data">上传并回填数据</button>
    <!-- <vxe-button size="mini" status="primary" style="height:25px; margin: 0 0 2px 15px;" @click="uploadAndFillData">导入</vxe-button> -->
    <!-- 表单或区域来显示回填的数据 -->
    <div v-if="formData">
      <!-- 使用formData来显示或更新UI -->
      <!-- <p>数据: {{ formData.someKey }}</p>  -->
      <!-- 如果数据是只读的,可以这样显示 -->
      <div>
        
        <p><strong>发票号码:</strong> {{ formData.number }}</p>
        <p><strong>开票日期:</strong> {{ formData.date }}</p>
        <p><strong>总金额:</strong> {{ formData.totalAmount }}</p>
        <p><strong>备注:</strong> {{ formData.note }}</p>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios';  
export default {
  data() {
    return {
      selectedFile: null,
      formData: null, // 用于存储从后端返回的数据
    };
  },
  methods: {
    onFileChange(e) {
      this.selectedFile = e.target.files[0];
    },

    uploadAndFillData() {
      // 检查文件是否已经选择
      if (!this.selectedFile) {
        alert("请先选择一个文件!");
        return;
      }

      const formData = new FormData(); // 创建一个FormData 对象,用于构建将要发送的数据
      formData.append("file", this.selectedFile); // 将文件添加到FormData中,字段名为file

      // this.$http({
      //       url: `/api/invoice/upload`,
      //       method: 'post',
      //       data: formData,
      //       headers: {
      //         'Content-Type': 'multipart/form-data'
      //         // 'Content-Type': 'application/json'
      //       },
      //       // enctype:"multipart/form-data",
      //     })

      axios.post('http://localhost:8080/invoice/upload', formData, {  
        headers: {  
          'Content-Type': 'multipart/form-data'  
        }  
      })  
      .then((response) => {
          this.formData = response.data; // 将解析后的数据保存到formData中
        }).catch((error) => {
          console.error("Error uploading file:", error);
          alert("上传文件时发生错误!");
        });
    },
  },
};
</script>

controller层中注解的配置

@CrossOrigin(origins = "http://localhost:8081", allowedHeaders = "*", allowCredentials = "true")
    @PostMapping("/upload")
    public ResponseEntity<Object> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 调用你的文件解析服务
            InvoiceSubset parsedData = invoiceService.parsePdfFile(file);

            // 返回解析后的数据
            return ResponseEntity.ok(parsedData);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error parsing file");
        }
    }

如此该问题得到解决

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值