1

tl;dr: Using Angular 6 on the front end and PHP with Phalcon on the backend, I can send JSON data or a File with no problem but I am having a problem sending both in the same request.


Previously I was sending JSON data to the server using something like this

const HTTP_OPTIONS = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json'
  }),
  observe: 'response'
};

post(endPoint: string, body: object): Observable<any> {
    return this.http.post<any>(this.apiUrl + endPoint, body, HTTP_OPTIONS)
      .pipe(
        tap(result => this.log(JSON.stringify(result, null, 2))),
        catchError(this.handleError('post', []))
      );
}

And I was able to get the data from PHP using Phalcon with

$app = new \Phalcon\Mvc\Micro();
$app->post('/upload', function() use ($app) {

    $input = $app->request->getJsonRawBody();
    // $input now contains my JSON data
});

Some time later, I needed to send a file so I used this answer with some minor modifications:

postFile(fileToUpload: File, endpoint: string): Observable<any> {
    const formData: FormData = new FormData();
    formData.append('fileKey', fileToUpload, fileToUpload.name);
    return this.httpClient
      .post(endpoint, formData, { headers: {'Authorization': this.jwt} }).pipe(
        tap(result => this.log(JSON.stringify(result, null, 2))),
        catchError(this.handleError('post', []))
      );
  }

And I received my file with no problems using the documentation as a guide:

$app->post('/uploads', function() use ($app) {
    if ($app->request->hasFiles() == true) {
        foreach ($app->request->getUploadedFiles() as $file) {
            $file->moveTo('files/' .$file->getname());
        }
    } else {
      $app->response->setStatusCode(400)->sendHeaders();
      $app->response->setJsonContent(['error' => 'no file']);
      return $app->response;
    }
});

The problem: Now I would like to send both a file and some JSON data at the same time. I can always just upload the file and then send the data separately but I don't think that's the right way to do it. I don't want to make more than the minimum number of network calls.

What I've tried: Using the file upload code and simply appending another field to my FormData object with my JSON data

formData.append('fileKey', fileToUpload, fileToUpload.name);
formData.append('data', JSON.stringify(data));

and a variation of that

formData.append('fileKey', fileToUpload, fileToUpload.name);
formData.append('data', new Blob([JSON.stringify(data), {type: 'application/json'}]);

Either way, on the backend I can get the file but $app->request->getJsonRawBody and $app->request->getRawBody are empty.

I also tried using the original JSON-sending code and just changing a bit to include the file but with no success.

post(fileToUpload: File, data: CustomData): Observable<any> {
    this.messageService.add('uploaded file');

    const formData: FormData = new FormData();
    formData.append('fileKey', fileToUpload, fileToUpload.name);
    formData.append('data', JSON.stringify(data), 'data');

    return this.http
      .post(this.apiUrl + 'uploads/', {'data': data, 'file': fileToUpload}, HTTP_OPTIONS).pipe( // file is empty on the server like this
        tap(result => this.log('POST file :\n' + JSON.stringify(result, null, 2))),
        catchError(this.handleError('post', [], 'fileUpload'))
      );
  }

I can easily send either my JSON data or the file but not both. I searched the Phalcon documentation and several QAs on sending files and/or JSON with Angular but I cannot figure out how to make this work.

Stack Underflow
  • 2,363
  • 2
  • 26
  • 51
  • 1
    Try looking in `$_POST['data']` or `$_FILES` for a file literally named "data". It looks like you're mixing methodology for multipart-encoded POSTs and just cramming JSON data into a raw POST. Stick with multipart, as it both allows you to upload multiple things, and PHP internals will handle things far more gracefully than you can in userland. – Sammitch Jan 14 '19 at 23:38
  • @Sammitch Thank you for your suggestion, it was very helpful. I was able to get my data using `file_get_contents($_FILES['data']['tmp_name'])` but appended onto the end of the JSON was `[object Object]` which I had to strip off before I could json_decode it. – Stack Underflow Jan 15 '19 at 17:25

2 Answers2

0

You are sending json as text in post request, so instead of $app->request->getJsonRawBody you should try something like

$rawJson=$app->request->getPost('data');
$object=json_decode($rawJson);
  • I appreciate you taking time to answer. The `$_POST` variable is empty as is `$app->request->getPost('data')` I was finally able to get the data as a string using @Sammitch suggestion looking in `$_FILES['data']` but it had `[object Object]` appended onto the end of it which was not there is the javascript when I sent it. So a make-shift solution is to take a substring, stripping of the `[object Object]` and I am able to json_decode it and all is well. Not the most elegant solution and I'm hoping for something better but at least it gets the job done for now. – Stack Underflow Jan 15 '19 at 17:17
  • Great that you find a solution. It was in $_FILES['data'] when you was sending it with `formData.append('data', JSON.stringify(data), 'data');` or with `formData.append('data', new Blob([JSON.stringify(data), {type: 'application/json'}]);` ? – Błażej Kowalczyk Jan 17 '19 at 22:10
  • I think with `formData.append('data', JSON.stringify(data));` (whithout third parameter, it is only for files) it should be available via $_POST and whithout `[object Object]` – Błażej Kowalczyk Jan 17 '19 at 22:17
  • 1
    I used `formData.append('data', new Blob([JSON.stringify(data), {type: 'application/json'}]);`. Without using blob, I was unable to get it either in $_FILES or $_POST. – Stack Underflow Jan 18 '19 at 14:55
0

you can get your json as @Błażej Kowalczyk said

$this->request->getPost()

and you can check for files and get them

if ($this->request->hasFiles()) {
    foreach ($this->request->getUploadedFiles() as $file) {
        // $file is an instance of Phalcon\Http\Request\File
        var_dump($file->getName());
    }
}

check these pages for more information https://docs.phalconphp.com/3.4/en/api/phalcon_http_request https://docs.phalconphp.com/3.4/en/api/phalcon_http_request_file

Talal
  • 394
  • 1
  • 13