简体   繁体   中英

POST request with multipart data in spring mvc

I am using ng-file-upload in Angular on client side to send a file(image,text etc) to Spring Boot Application.

I have Client side running in Xampp with url "localhost" while have spring instance running separately with url "localhost:8080".Cors on both sides are enabled and other all requests are successfully entertained.

Client Side Code:

        Upload.upload({
        url: 'http://localhost:8080/file/upload',
        method:'POST',
            data: {
            uploadedPicture: file,
            uploadedFrom: 'recipe'
        },
    }).then(function(response) {
        $timeout(function() {
            $scope.result = response.data;
        });
    }, function(response) {
        if (response.status > 0) $scope.errorMsg = response.status + ': ' + response.data;
    }, function(evt) {
        $scope.progress = parseInt(100.0 * evt.loaded / evt.total);
    });

Server Side Code:

@CrossOrigin
@RequestMapping(method = RequestMethod.POST, value = "/file/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
                               RedirectAttributes redirectAttributes) {
    if (!file.isEmpty()) {
        try {
            Files.copy(file.getInputStream(), Paths.get(ROOT, file.getOriginalFilename()));
            redirectAttributes.addFlashAttribute("message",
                    "You successfully uploaded " + file.getOriginalFilename() + "!");
        } catch (IOException|RuntimeException e) {
            redirectAttributes.addFlashAttribute("message", "Failued to upload " + file.getOriginalFilename() + " => " + e.getMessage());
        }
    } else {
        redirectAttributes.addFlashAttribute("message", "Failed to upload " + file.getOriginalFilename() + " because it was empty");
    }

    return "redirect:/";
} 

I have tried the cors by sending get request from same code to the same resource with get method which is working.but when i send post request with a multipart form data (image or any other file) it rejects OPTIONS request.

   OPTIONS http://localhost:8080/file/upload
   XMLHttpRequest cannot load http://localhost:8080/file/upload. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 403.

I have Tested This resource by postman also and it uploaded file without error.

EDIT: I have tried by changing the http to https and it is giving the error as OPTIONS https://localhost:8080/file/upload net::ERR_TIMED_OUT_ problem is same as it cannot find the required resource

Any thoughts over this issue??

Looking at your error message I see:

No 'Access-Control-Allow-Origin' header is present

Are you sure the proper headers are being added?

We used a Filter to ensure all requests had these Headers added correctly:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: POST,GET, OPTIONS, DELETE
  • Access-Control-Max-Age: 3600
  • Access-Control-Allow-Headers: x-requested-with

Here is the Filter class we used:

SimpleCORSFilter.java

 @Component
 public class SimpleCORSFilter implements Filter {

        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
            chain.doFilter(req, res);
        }

        public void init(FilterConfig filterConfig) {}

        public void destroy() {}

    }

只需将@CrossOrigin批注更改为:

@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.OPTIONS}, allowedHeaders = {"Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"}, exposedHeaders = {"Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"})

First check your security settings, since you obviously get 403 as Status Code. Maybe your redirect at the end causing problems, try without it.

Here is a code sample, I was using a while ago to add CORS-headers:

@RequestMapping(value="/GetSomething", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
public ResponseEntity<String> getSomething() throws IOException {
    HttpHeaders responseHeaders = new HttpHeaders();
    //need for cross-domain requests
    responseHeaders.add("Access-Control-Allow-Origin", "*");
    //this one is needed, if your browser should send cookies
    responseHeaders.add("Access-Control-Allow-Credentials", "true");
    ...
    responseHeaders.setContentLength(resp.getBytes("UTF-8").length);
    return new ResponseEntity<String>(resp, responseHeaders, HttpStatus.OK);
}

EDIT: removed session-parameter

EDIT2: can you check, if the code in your POST-request is actually being executed? Make some logs in the function. If your code is not executed, then you clearly have security setup issue and your request doesn't go through security layer. CORS is client-based functionality, it can only block data AFTER the browser get it from server!

I had faced this problem before in my current project, after a lot RND and some experiments, i have found that the problem was with the content type, whenever there is a image upload will take a place to upload content type is matter a much here.

I haven't test this, but let me know after try this by replace in your code near...

            var fd = new FormData();
            fd.append('uploadedPicture', file);
            fd.append('uploadedFrom', 'recipe');

            Upload.upload({
                url: 'http://localhost:8080/file/upload',
                method: 'POST',
                data: fd,
                transformRequest: angular.identity,
                headers: {'Content-Type': undefined},
            }).then(function (response) {
                $timeout(function () {
                    $scope.result = response.data;
                });
            }, function (response) {
                if (response.status > 0)
                    $scope.errorMsg = response.status + ': ' + response.data;
            }, function (evt) {
                $scope.progress = parseInt(100.0 * evt.loaded / evt.total);
            });

Let me know please after try this.

It makes no sense to return a redirect from an Ajax call. Firstly determine if the issue is with the form post or with the subsequent action. To do so change your Controller as below:

@CrossOrigin
@RequestMapping(method = RequestMethod.POST, value = "/file/upload")
@ResponseBody
public String handleFileUpload(@RequestParam("file") MultipartFile file) {

    String status = null;

    if (!file.isEmpty()) {
        try {
            Files.copy(file.getInputStream(), Paths.get(ROOT, file.getOriginalFilename()));
            status = "okay";

        } catch (IOException|RuntimeException e) {
            status = "error";  
        }
    } else {
        status = "error";
    }

    return "status"; // a literal string
}

I would also note that the params sent from the front-end do not match what the backend expects. Where in the front-end do you specify a parameter named 'file' which is what the controller is going to map to the specified RequestParam?

The library in question has an end to end example using Spring MVC here:

https://github.com/danialfarid/ng-file-upload/wiki/spring-mvc-example

Note how the parameter names match on both sides.

添加内容类型,请参阅此答案,可能对您有所帮助。 请求未找到ajax spring mvc和origins =“ / **”

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM