将文件发布到 spring 控制器时,当前请求不是多部分

Current request is not a multipart when posting a file to a spring controller

我正在尝试向服务器发送多部分请求,但出现以下异常

org.springframework.web.multipart.MultipartException: Current request is not a multipart request
    at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:188)
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:104)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)

根据教程和 Whosebug 问题,我开发了以下代码

控制器

  @RequestMapping(value = "/upload-file", method = RequestMethod.POST)
    public ResponseEntity<Void> getUploadFile(@RequestParam("file") MultipartFile file) {
    LOGGER.debug(String.valueOf(file));}

多部分配置

@Configuration
@EnableWebMvc
public class MultipartConfiguration {

    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final long MAX_UPLOAD_FILE_SIZE = 100000000;

    @Bean
    public CommonsMultipartResolver multipartResolver() {
        CommonsMultipartResolver resolver=new CommonsMultipartResolver();
        resolver.setDefaultEncoding("utf-8");
        resolver.setMaxUploadSize(MAX_UPLOAD_FILE_SIZE);
        resolver.setDefaultEncoding(DEFAULT_ENCODING);
        return resolver;
    }
}

html

<input type="file"  style="display:none" #fileupload name="image" (change)="fileProgress($event)" />

组件

export class FileUploadComponent {
  fileData: any = null;
  uploadedFilePath: string = null;
   formData = new FormData();
    reader = new FileReader();
  @Output() fiLoaded: EventEmitter<File> = new EventEmitter();
  constructor(private pointeService: PointeService) { }

  fileProgress(fileInput: any) {

    this.fileData = fileInput.target.files[0];

    this.reader.readAsDataURL(this.fileData);
    this.reader.onloadend = (_event) => {
      this.formData.append('file', this.fileData);
      this.onFileLoaded(this.formData);
    };
  }

  onFileLoaded(formData: any) {
    this.pointeService.postModifiedFileExel(formData)
      .subscribe(events => {
        if (events.type === HttpEventType.UploadProgress) {
        } else if (events.type === HttpEventType.Response) {
         // this.fileUploadProgress = 0;
          console.log(events.body);
          alert('SUCCESS !!');
        }

      });
  }
}

服务

postModifiedFileExel(formData: any) {
    return this.http.post(`${AppUtils.REST_API_SERVER}/espacegroupe/upload-file`, formData ,  {
      reportProgress: true,
      observe: 'events'
    });
  }

我还注意到我的 post 只包含文件名,不包含字节格式的数据

有什么建议吗?

对于那些仍然卡在同一个问题上的人,我将首先解释我是如何调试以找到解决方案的,其次是我做错了什么。

调试

当我处理堆栈跟踪时,我发现了两种方法负责我收到的消息,即 Current request is not a multipart request

public class ServletFileUpload extends FileUpload {
        private static final String POST_METHOD = "POST";

        public static final boolean isMultipartContent(HttpServletRequest request) {
            return !"POST".equalsIgnoreCase(request.getMethod()) ? false : FileUploadBase.isMultipartContent(new ServletRequestContext(request));
        }
      .....
}

    public abstract class FileUploadBase {

        public FileUploadBase() {
        }

        public static final boolean isMultipartContent(RequestContext ctx) {
            String contentType = ctx.getContentType();
            if (contentType == null) {
                return false;
            } else {
                return contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/");
            }
        }
     ....
}

这意味着如果您的调用不同于 POST 或者您的内容类型不是以 "multipart/" 开头,您的播放量将被视为非 multipart

我注意到我的内容类型总是 Content-Type: application/json

但是为什么我总是 Content-Type: application/json 认为 formData 应该是 Content-Type: multipart/form-data

在我的例子中,这是因为在应用程序中有一个 url 拦截器,它总是在 header Content-Type: application/json.

中设置
const authReq = req.clone({setHeaders: {'Content-Type': 'application/json'}, withCredentials: true});

为确保您发送正确的数据类型,请确保在请求 Header 中您有 Content-Type: multipart/form-data;boundary=... 并且您的 formData 是二进制的。

这里有一篇 link 的文章帮助我解决了这个问题