0

I'm trying to upload a file from AngularJS frontend to Spring MVC backend, using Tomcat 8 server.

This is my AngularJS controller function:

vm.add = function(photo) {
                let file = document.getElementById('file').files[0];
                let reader = new FileReader();
                reader.onload = function(e) {
                    let photoFile = e.target.result;
                    photoService.create($routeParams.lid, photo,photoFile).then(
                            function(response) {
                                $location.path('/books');
                                vm.photo = response.data;
                            })
                }
                reader.readAsBinaryString(file);
            }

This is my service function, still on the AngularJS side:

service.create = function(bookId, photo, file) {
            var formData = new FormData();
            formData.append('name', photo.name);
            formData.append('file', file);
            return $http(
                    {
                        url : BASE_URL + authService.getToken().id
                        + '/book/' + bookId + '/photo/',
                        method : 'POST',
                        data : formData,
                        headers : {
                            'Content-Type' : undefined
                        },
                        transformRequest: angular.identity
                    }).then(function(res) {
                return res;
            });
         };

I have this bean in my dispatcher xml:

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

    <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="10000000" />
</bean>

And this in my Spring MVC controller:

@Override
@RequestMapping(value = "user/{uid}/book/{lid}/photo", method = RequestMethod.POST)
public Photo create(HttpServletRequest request, HttpServletResponse response, 
        @PathVariable int uid, @PathVariable int lid, 
        @RequestParam("file") MultipartFile file) {

    response.setHeader("Access-Control-Allow-Origin", request.getHeader("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, Content-Type");
    return photoDAO.create(uid,lid,file);
}

I can send the file and save it just fine directly from the html, but for some reason sending it from AngualrJS gives me this error on the Spring side:


2018-09-02 17:45:00,027 DEBUG [org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod] - Error resolving argument [4] [type=org.springframework.web.multipart.MultipartFile]
HandlerMethod details: 
Controller [controllers.PhotoControllerImpl]
Method [public entities.Photo controllers.PhotoControllerImpl.create(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,int,int,org.springframework.web.multipart.MultipartFile)]

org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present

Even though I do see the file in my request payload:


018-09-02 17:45:00,028 DEBUG [com.github.isrsal.logging.LoggingFilter] - Request: request id=4; method=POST; content type=multipart/form-data; boundary=----WebKitFormBoundaryfNOybpMz7ZO1IyZX; uri=/InfiniteLegacyREST/api/user/1/legacy/1/photo/; payload=------WebKitFormBoundaryfNOybpMz7ZO1IyZX
Content-Disposition: form-data; name="name"

s
------WebKitFormBoundaryfNOybpMz7ZO1IyZX
Content-Disposition: form-data; name="file"

this is a text

------WebKitFormBoundaryfNOybpMz7ZO1IyZX--

What is causing this? Why does uploading the file in a multipart/form-data directly submitted from the html work just fine but doing it from AngularJS doesn't?

Shant Dashjian
  • 888
  • 11
  • 20

1 Answers1

0

Looking at your error message, that @RequestParam MultipartFile you're using in your public Photo create(...) controller method may need to be a @RequestPart instead:

@RequestPart(name = "file", required = false) MultipartFile file

My TightBlog app has a working example of an Spring & Angular-based file upload: REST controller, angular.js and JSP.

Glen Mazza
  • 748
  • 1
  • 6
  • 27
  • I changed it to `@RequestPart` and I still see the same error. The interesting thing as I mentioned earlier is that when I submit the file directly from the html, it works and the request logger shows that the payload contains the `Content-Type` for the file. When I send via AngularJS and $http, I see the error and there is no `Content-Type` added for the file in the payload. – Shant Dashjian Sep 03 '18 at 09:34
  • Unsure, but I think you're supposed to send the file via the form, i.e., no bug here. If someone accidentally chooses the wrong file from a choose file popup window, they want to be able to undo their action, not have a private file accidentally sent to your server as a result. – Glen Mazza Sep 03 '18 at 12:33
  • Yes, I do have a form, and when I submit it, the submit button, instead of submitting directly to the server, calls an AngularJS method that will do the submission. – Shant Dashjian Sep 03 '18 at 16:19
  • What you want here is a file upload without an HTML form submit (the fact that you're activating your Angular upon hitting the submit button isn't really a form submit of course), [this](https://stackoverflow.com/questions/35332886/uploading-file-in-angularjs-without-submit-button) topic may have some helpful links for you in the answers. – Glen Mazza Sep 03 '18 at 17:05