3

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

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

and the angular code (based on the discussion here):

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->file('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?

update

It works just fine using postman. this is the curl generated by postman:

postman curl:

curl -X POST \
  http://127.0.0.1:8000/api/stores/csv \
  -H 'Accept: application/x.toters.v1+json' \
  -H 'Authorization: Bearer ***' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Postman-Token: 4f9d2f3b-551b-aa47-4f65-98c7582f2919' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F file=@/Users/Shared/Download/bac-383-store-update.csv

postman server logs

[2018-03-03 06:35:18] local.INFO: ----------------------------- [] []
[2018-03-03 06:35:18] local.INFO: POST api/stores/csv [] []
[2018-03-03 06:35:18] local.INFO: array (   'file' =>    Illuminate\Http\UploadedFile::__set_state(array(      'test' => false,      'originalName' => 'bac-383-store-update.csv',      'mimeType' => 'text/csv',      'size' => 62,      'error' => 0,   )), ) [] []
[2018-03-03 06:35:18] local.INFO: Status code: 200 [] []
[2018-03-03 06:35:18] local.INFO: User id: 104 [] []

this is the curl generated by restangular

restangular (two requests - using chrome) curl:

curl 'http://127.0.0.1:8000/api/stores/csv' 
-X OPTIONS 
-H 'Access-Control-Request-Method: POST' 
-H 'Origin: http://localhost:3000' 
-H 'Accept-Encoding: gzip, deflate, br' 
-H 'Accept-Language: en-US,en;q=0.9' 
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36' 
-H 'Accept: */*' 
-H 'Connection: keep-alive' 
-H 'Access-Control-Request-Headers: authorization,content-type' 
--compressed


curl 'http://127.0.0.1:8000/api/stores/csv' 
-H 'Origin: http://localhost:3000' 
-H 'Accept-Encoding: gzip, deflate, br' 
-H 'Accept-Language: en-US,en;q=0.9' 
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEwNCwiaXNzIjoiaHR0cDpcL1wvMTkyLjE2OC4wLjIzMjo4MDAwXC9hcGlcL3VzZXJzXC9sb2dpbiIsImlhdCI6MTUxOTk2OTAzNiwiZXhwIjoxNjE0NTc3MDM2LCJuYmYiOjE1MTk5NjkwMzYsImp0aSI6IndHV0UzRjhYQ3hRdDBGOWMifQ.XqySEIprbxtAU-RfhOgtFkScN1nUuuXEwMxCltfjqu8' 
-H 'Content-Type: application/json' 
-H 'Accept: application/x.toters.v1+json' 
-H 'Referer: http://localhost:3000/?local/' 
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36' 
-H 'Connection: keep-alive' 
--data-binary $'------WebKitFormBoundary9ApCHw2ykmLYK2IY\r\nContent-Disposition: form-data; name="file"; filename="bac-383-store-update.csv"\r\nContent-Type: text/csv\r\n\r\n\r\n------WebKitFormBoundary9ApCHw2ykmLYK2IY--\r\n' --compressed

restangular/chrome server logs

[2018-03-03 06:34:52] local.INFO: ----------------------------- [] []
[2018-03-03 06:34:52] local.INFO: OPTIONS api/stores/csv [] []
[2018-03-03 06:34:52] local.INFO: array ( ) [] []
[2018-03-03 06:34:52] local.INFO: Status code: 200 [] []
[2018-03-03 06:34:52] local.INFO: ----------------------------- [] []
[2018-03-03 06:34:52] local.INFO: POST api/stores/csv [] []
[2018-03-03 06:34:52] local.INFO: array ( ) [] []
[2018-03-03 06:34:52] local.INFO: Status code: 200 [] []
[2018-03-03 06:34:52] local.INFO: User id: 104 [] []

update 2:

after comparing the headers between both curls, i noticed that the content type was different:

postman curl snippet

-H 'Content-Type: application/x-www-form-urlencoded' \

restangular curl snippet

-H 'Content-Type: application/json' 

this was shocking, considering that the rectangular people PROMISED that by using the above syntax, the content type should be application/x-www-form-urlencoded:

Restangular.all('users') .withHttpConfig({transformRequest: angular.identity}) .customPOST(formData, undefined, undefined, { 'Content-Type': undefined }); This basically tells the request to use the Content-Type: multipart/form-data as the header.

so I wrote this:

    API.all('stores/csv').withHttpConfig({transformRequest: angular.identity})
    .customPOST(formData, undefined, undefined,
      { 'Content-Type': 'application/x-www-form-urlencoded' }).then((response) => {console.log(response);

this improved things a bit.. compare the two curls now:

postman

curl -X POST \
  http://127.0.0.1:8000/api/stores/csv \
  -H 'Accept: application/x.toters.v1+json' \
  -H 'Authorization: Bearer ***' \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'Postman-Token: 4f9d2f3b-551b-aa47-4f65-98c7582f2919' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F file=@/Users/Shared/Download/bac-383-store-update.csv

angular

  curl 'http://127.0.0.1:8000/api/stores/csv' 
  -H 'Origin: http://localhost:3000' 
  -H 'Accept-Encoding: gzip, deflate, br' 
  -H 'Accept-Language: en-US,en;q=0.9' 
  -H 'Authorization: Bearer ***
  -H 'Content-Type: application/x-www-form-urlencoded' 
  -H 'Accept: application/x.toters.v1+json' 
  -H 'Referer: http://localhost:3000/?local/' 
  -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36' 
  -H 'Connection: keep-alive' 
  --data $'------WebKitFormBoundaryd405EnTxpFJv2GDN\r\nContent-Disposition: form-data; name="file"; filename="bac-383-store-update.csv"\r\nContent-Type: text/csv\r\n\r\n\r\n------WebKitFormBoundaryd405EnTxpFJv2GDN\r\nContent-Disposition: form-data; name="name"\r\n\r\nfile\r\n------WebKitFormBoundaryd405EnTxpFJv2GDN--\r\n' 
  --compressed

so i'm still not sending the -F option

abbood
  • 21,507
  • 9
  • 112
  • 218
  • I'm not sure what arguments `customPOST` takes, but have you tried `customPOST(formData, undefined, undefined, ...)` using `undefined` instead of an empty string, or `'file'` as the second argument? Also, this example may help https://github.com/mgonto/restangular/issues/1169#issuecomment-156360136 – Kyle Finley Mar 03 '18 at 15:26

2 Answers2

3

I've not touched on restangular before, however in Laravel, when you want to get a file from the request you need to use the file method on the request object:

$file = $request->file('file');

That should give you an instance of Illuminate\Http\UploadedFile

Jonathon
  • 13,610
  • 9
  • 63
  • 84
1

I haven't delved into restangular a lot but on an HTTP level, I believe you want your Content-Type header to be multipart/form-data, not application/x-www-form-urlencoded.

If you're curious about the difference, check this answer out.
Since you're sending a file, multipart/form-data is what you want.


I know you see Postman do:

-H 'Content-Type: application/x-www-form-urlencoded'

But just a few lines under that, it does:

-H 'content-type: multipart/form-data; boundary...
rodyhaddad
  • 186
  • 2
  • What's the difference between upper case and lower case content-type? – abbood Mar 03 '18 at 18:06
  • @abbood: Nothing, HTTP headers are case-insensitive ([Source](https://tools.ietf.org/html/rfc7230#section-3.2)) – rodyhaddad Mar 04 '18 at 16:14
  • So then how did it make sense that the same request has two different values for the same header? – abbood Mar 04 '18 at 16:36
  • HTTP Requests can have duplicate headers. It makes sense for things like `Cache-Control` or `Cookie`, but doesn't much for `Content-Type`. So I'm guessing web servers just handle it by using the last specified value of the header. Not sure why postman is putting the same header twice though here, might be worth a PR to fix it :) – rodyhaddad Mar 05 '18 at 18:31