Skip to content

multipart form data submit with restangular #420

Closed

Description

@zoheb

I have to submit a form which includes a user selected file to the server. I can get this working using $http directly as below,

    var fd = new FormData();
            fd.append("profile",angular.toJson(profile));
             fd.append("file", file);
            return $http.post("/server/api/profile/me/bio", fd, {
                withCredentials: true,
                headers: {'Content-Type': undefined },
                transformRequest: angular.identity
            });

I tried to do the same submit using restangular as below
return userAPI.one('me').customPOST(fd,"bio",{}, {'Content-type':undefined});

However this doesn't do the multipart submit correctly and attempts to send a empty payload request. I suspect this is because I need to specify the transformRequest flag to address this issue: angular/angular.js#1587

Is there a way to do set a request transformer just on this request?

Thanks

Activity

  1. mgonto commented on Nov 21, 2013

    @mgonto
    Owner

    Hey,

    You can do that in the latest version of Restangular

    userAPI.one('me').withHttpConfig({transformRequest: angular.identity}).customPOST(...`

    That should work for you :).

    Also, all other $httpConfigurations can be set in that withHttpConfig call as a parameter.

    Please tell me if it worked and reopen if it didn't

  2. zoheb commented on Nov 21, 2013

    @zoheb
    Author

    Thanks for the reply.

    I updated to the latest version and changed to use withHttpConfig as you mentioned, but it still didn't quite work. Now the request payload is not empty but has the string "[object Object]". Note that the object I am passing in ("fd") is a FormData object.

  3. reopened this on Nov 21, 2013
  4. mgonto commented on Nov 21, 2013

    @mgonto
    Owner

    Did you set there the withCredentials and contentType as well?

    It should be the same with those.

    Can you debug https://github.com/mgonto/restangular/blob/master/src/restangular.js#L457 and check what's being sent in to $http? You can compare that to see what's going on.

  5. zoheb commented on Nov 21, 2013

    @zoheb
    Author

    Everything seems to be the same except the "data" attribute shows up in the debugger as "Object" instead of "FormData" when I used $http directly and put a breakpoint in angular.js. Since it's no longer a FormData object I think $http skips all the multipart handling stuff.

    I wasn't able to figure out where the passed in FormData object was being changed in the restangular codebase. When a formdata object is passed in think it should just preserve it.

  6. zoheb commented on Nov 21, 2013

    @zoheb
    Author

    Typo - I meant is shows up as "Object" when using restangular, but shows up correctly as FormData object when using $http

  7. mgonto commented on Nov 21, 2013

    @mgonto
    Owner

    I know what it's. The problem is that I'm stripping Restangular stuff. As a hack go to elemFunction and remove StripRestangular part. 

    If that works Ill get it fixed :) 

    —
    Mobile mail. Excuse brevity and typos.

    On Thu, Nov 21, 2013 at 8:28 PM, zohebsait notifications@github.com
    wrote:

    Typo - I meant is shows up as "Object" when using restangular, but shows up correctly as FormData object when using $http

    Reply to this email directly or view it on GitHub:
    #420 (comment)

  8. zoheb commented on Nov 21, 2013

    @zoheb
    Author

    Yes! I temporarily commented out lines 918-921 shown below and it's works! I will work around till you push a fix. Thanks for your help.

    //                  if (_.isObject(callObj)) {
    //                      callObj = stripRestangular(callObj);
    //                  }
    
  9. mgonto commented on Nov 21, 2013

    @mgonto
    Owner

    Thank you. 

    Ill leave this open meanwhile

    —
    Mobile mail. Excuse brevity and typos.

    On Thu, Nov 21, 2013 at 8:44 PM, zohebsait notifications@github.com
    wrote:

    Yes! I temporarily commented out lines 918-921 shown below and it's works! I will work around till you push a fix. Thanks for your help.

    //                  if (_.isObject(callObj)) {
    //                      callObj = stripRestangular(callObj);
    //                  }
    

    Reply to this email directly or view it on GitHub:
    #420 (comment)

  10. alonisser commented on Dec 4, 2013

    @alonisser

    was this fixed?

  11. mgonto commented on Dec 4, 2013

    @mgonto
    Owner

    Not yet, please comment those lines temporarily. I haven’t had much time lately to work on this bugs, but I’ll try to work on them ASAP.


    Martin Gontovnikas
    Software Engineer
    Buenos Aires, Argentina

    Twitter: @mgonto (https://twitter.com/mgonto)
    Linkedin: http://www.linkedin.com/in/mgonto
    Github: https://github.com/mgonto

    On Wednesday, December 4, 2013 at 9:58 AM, Alonisser wrote:

    was this fixed?

    —
    Reply to this email directly or view it on GitHub (#420 (comment)).

  12. mgonto commented on Dec 9, 2013

    @mgonto
    Owner

    Pushing fix :)

  13. alonisser commented on Dec 9, 2013

    @alonisser

    great!

    Twitter:@alonisser https://twitter.com/alonisser
    LinkedIn Profile http://www.linkedin.com/in/alonisser
    Facebook https://www.facebook.com/alonisser
    _Tech blog:_4p-tech.co.il/blog
    _Personal Blog:_degeladom.wordpress.com
    Tel:972-54-6734469

    On Mon, Dec 9, 2013 at 10:49 PM, Martin Gontovnikas <
    notifications@github.com> wrote:

    Pushing fix :)

    —
    Reply to this email directly or view it on GitHubhttps://github.com//issues/420#issuecomment-30171863
    .

  14. deltaepsilon commented on Jan 24, 2014

    @deltaepsilon

    This thread just saved my bacon, but I wasted some time trying to figure out how to set the headers correctly. Here's a solution that worked for me. It's Coffeescript. Sorry. Not my choice.

        postIt = () ->
          formData = new FormData()
          formData.append('file', file) # file is an ArrayBuffer read with fileReader.readAsArrayBuffer(file)
          formData.append('name', file.name)
    
          apiAuth.one('api/client', client.id).one('note', note.id).withHttpConfig({transformRequest: angular.identity}).customPOST(formData, 'file', undefined, {'Content-Type': undefined}).then (res)->
            deferred.resolve(res)
    
  15. gen4sp commented on Jan 18, 2015

    @gen4sp

    It still here, guys. I have same problem with 1.4
    What i can do with it?

    Restangular.all('points').withHttpConfig({transformRequest: angular.identity}).customPOST(formData,'',undefined,{'Content-Type': undefined}).then(function (response) {
    
  16. ncourtial commented on Jan 23, 2015

    @ncourtial

    Hi,
    All seems to be OK when you don't set global default headers.
    When adding the following default values in config step, form data are posted without requested headers : form-data and boundary but with 'application/json'.
    RestangularProvider.setDefaultHeaders({
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest"
    });

    When removing "Content-Type": "application/json" from config , customPOST(formData,'',undefined,{'Content-Type': undefined}) does its stuff.

    Any idea?

    Many thanks in advance.

    Cheers

  17. Tallyb commented on Jan 24, 2015

    @Tallyb

    +1

  18. diosney commented on Feb 4, 2015

    @diosney

    Hi!

    I'm trying to implement this but can't figure it out with my use case since it seems that Restangular.service('entities') doesn't have a valid .withHttpConfig() method.

    How can achieve that functionality using the service approach?

    Thanks in advance.

  19. diosney commented on Feb 6, 2015

    @diosney

    It seems that Restangular.service('entities') only exports getList(), get(), post() methods :(

    Nevertheless, in my Entities service I changed Restangular.service('entities') for Restangular.all('entities') and the file upload worked like a charm.

  20. steinerj commented on Mar 2, 2015

    @steinerj

    I'm having the exact same issue with using httpConfigs for objects created with .service

    I have provided a PR, because I think this is a bit of a inconsistency. See #1068

  21. augnustin commented on Mar 3, 2015

    @augnustin

    Thanks for comments and code snippets. It helps quite a lot!

    I have a working implementation but I'm halfly satified because it uses Base64-encoded data upload which seems less efficient than Multipart Upload, and would not work with heavy file (as far as I understood).

    @deltaepsilon I tried your code but it only sends the file itself, whereas I would like to update my user during the same call. It should be possible as that's what a regular form does.

    Here's the code:

    View:

    <img ng-src="{{user.avatar}}" class="img-circle img-responsive">
    <input type="file" name="avatar" onchange="angular.element(this).scope().updateClientImage(this.files)" />
    <input class="btn btn-success btn-lg" type="submit" value="Save" ng-click="save_user()" />
    

    Controller:

            $scope.save_user = function() {
                var deferred;
                // if ($scope.file) {
                //     var formData = new FormData();
                //     formData.append('user[avatar]', $scope.file);
                //     deferred = $scope.user.withHttpConfig({transformRequest: angular.identity}).customPUT(formData, undefined, undefined, {'Content-Type': undefined});
                // } else {
                    deferred = $scope.user.put();
                // }
                deferred.then(function(res) {
                    console.log(res.msg);
                }, function(err){
                    console.log('An error occured:');
                    console.log(err);
                });
            };
    
            $scope.updateClientImage = function(files) {
                $scope.file = files[0];
                var reader = new FileReader();
                reader.onload = function(e) {
                    $scope.user.avatar = e.target.result;
                    $scope.$apply();
                };
                reader.readAsDataURL($scope.file);
            };
    

    Simply calling $scope.user.put() sends a Base64-encoded image as a user.avatar attributes, which works Ok in my backend.

    But how can I have a customPUT that sends over the user attributes + the formData - user.avatar (otherwise it is sent twice)?

     $scope.user.withHttpConfig({transformRequest: angular.identity}).customPUT(formData, undefined, undefined, {'Content-Type': undefined});
    

    I'll report final working solution. Thanks a lot!

  22. ArthurianX commented on May 29, 2015

    @ArthurianX

    Any progress on this? I'm having the same problem. :(

  23. Waxo commented on Jun 19, 2015

    @Waxo

    I'm using jsPDF to create a PDF then i try to submit it with return Restangular.all('users').one('pdf', id).withHttpConfig({transformRequest: angular.identity}).customPOST(fd, 'submit', undefined, {'Content-Type': undefined});
    But the header is application/json and i need a multipart/*
    If i force the Content-Type to multipart/form-data i have a boundary problem. Is there a workaround ?

  24. rhodul commented on Oct 16, 2015

    @rhodul

    @ncourtial, the default headers problem is fixed like so:
    postFile: function (dataUri, path, keyName, fileName) {
    var authenticatedHeaders = angular.extend({},
    DEFAULT_HTTP_HEADERS,
    {"x-authtoken": localStorage.authtoken});
    authenticatedHeaders["Content-Type"] = undefined;
    var formData = new FormData();
    var blob = dataURItoBlob(dataUri);
    formData.append(keyName, blob, fileName);
    // kill default headers
    var restangularRoute = Restangular.withConfig(function (RestangularConfigurer) {
    RestangularConfigurer.setDefaultHeaders(authenticatedHeaders);
    });
    return restangularRoute.one(path).withHttpConfig({transformRequest: angular.identity})
    .customPOST(formData, '', undefined, authenticatedHeaders);
    }

  25. suvarnajayanth commented on Mar 18, 2016

    @suvarnajayanth

    if i set ["Content-Type"] = undefined, It calls webservice , but i i want to send text data in other language (utf-8) it won't receive as expected in Rest api

  26. andrzej-aa commented on Jun 1, 2016

    @andrzej-aa

    I found a workaround for setting default headers. Instead of setting undefined, set a function returning undefined:

    RestangularProvider.setDefaultHeaders({
      'Content-Type': 'application/json'
    });
    
    
    return API.all(UPLOAD_RESOURCE)
      .withHttpConfig({transformRequest: angular.identity})
      .customPOST(file, 'image', {}, {
        'Content-Type': () => {
          return undefined;
        }
      });
    
  27. abbood commented on Mar 2, 2018

    @abbood

    I can't seem to have it work.. this is what I'm using:

    I'm using restangular 1.51 with angular 1.6. My html looks like this

    <input type="file" name="file" />
    

    and the angular code:

    let directive = {
      ..
      link: (scope, element, attrs) => {
            let inputElement = angular.element(element[0].querySelector('input'));
            inputElement.bind('change', function () {
            var formData = new FormData();
            formData.append('file', inputElement[0].files[0]);
    
            API.all('stores/csv').withHttpConfig({transformRequest: angular.identity})             .customPOST(formData,'' , undefined,
              { 'Content-Type': undefined }).then((response) => {console.log(response);
              });
        });
    

    laravel code:

    public function upload(Request $request)
    {
    
        $this->validate($request, [
            'file' => 'required',
            'type'  => 'in:csv,xls,xlsx',
            ]);
    
        $file = $request->input('file');
        var_dump($file);
        return response()->success(['file' => $file]);
    }
    

    thing is the $file here is appearing as an empty array in the laravel dump. The documentation is pretty bad on this. Ideas?

  28. abbood commented on Mar 3, 2018

    @abbood

    so basically i had to brute force the content-type to application/x-www-form-urlencoded..

    btw i'm using restangular 1.51.. more details here https://stackoverflow.com/questions/49077674/how-to-upload-files-with-angular-using-cors/49081727#49081727

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions