Home > Software engineering >  Attempting to upload a file from local to a server via php
Attempting to upload a file from local to a server via php

Time:08-16

I'm attempting to upload a file via node from my local system to a php based remote server.

My initial attempt:

  const readStream = fs.createReadStream(filePath);
  
  fetch(API_ENDPOINT, {
    method: 'POST',
    body: readStream
  })
  .then(function(res) {
    console.log(res);
    return "here";
    // return res.json();
  }).then(function(json) {
    console.log(json);
  })

This returns a 200 that it hit the server but on the php side I'm checking to see if it's reading and this file write appears empty.

<?php
file_put_contents('fileExistsNew', $_FILES);
file_put_contents('fileExistsPost', $_POST);

?>

I try using formData and that also doesn't work for me it gives me an error that it's not a blob.

Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'


 const stats = fs.statSync(filePath);
  const fileSizeInBytes = stats.size;
  const fileStream = fs.createReadStream(filePath);
  formData.append('file', fileStream, { knownLength: fileSizeInBytes });

  const options = {
    method: 'POST',
    body: formData
  }

  fetch(API_ENDPOINT, {...options})
  .then(res => {
    if(res.ok) return res;
    throw res;
  });

How can I accomplish this?

CodePudding user response:

Streaming Files

PHP populates $_FILES when it receives a multi-part encoded request. Your first attempt isn't sending one of those. It is sending the raw file data (and hasn't included the content-type header).

So let's start by tweaking that:

import fs from 'fs';
import fetch from 'node-fetch';

const API_ENDPOINT = 'http://localhost:5000/';
const filePath = './sample.png';

const readStream = fs.createReadStream(filePath);

fetch(API_ENDPOINT, {
    method: 'POST',
    body: readStream,
    headers: {
        'Content-Type': 'image/png',
    },
})
    .then(res => res.json())
    .then(data => {
        console.log(data);
    });

Then, on the PHP side, the data will be presented through standard input, so you can read it with PHP's file handling files.

Here's an example which copies the file data to a file on disk and then reports some statistics about it.

<?php
    $filename = "/tmp/outfile.png";

    $uploaded_file = fopen("php://input", "r");
    $out_file = fopen($filename, "w");
    while ($data = fread($uploaded_file, 1024))
        fwrite($out_file, $data);
    fclose($uploaded_file);
    fclose($out_file);

    $stats = stat($filename);

    header("Content-Type: application/json";
    echo json_encode($stats);
?>

Note that this completely lacks any form of error handling (it doesn't even check the content-type header I set in the previous section).

Using Form Data

Form data objects can be used to generate a multipart request which PHP will use to populate $_FILES.

it gives me an error that it's not a blob

I don't know what library you are using to provide FormData to Node.js, but formdata-polyfill expects the stream to be returned from a method on the object you pass to append:

import fs from 'fs';
import fetch from 'node-fetch';
import { FormData } from 'formdata-polyfill/esm.min.js';

const API_ENDPOINT = 'http://localhost:5000/multipart.php';
const filePath = './sample.png';

const stats = fs.statSync(filePath);
const fileSizeInBytes = stats.size;
const body = new FormData();
body.append('file', {
    size: fileSizeInBytes,
    name: 'example.png',
    stream() {
        return fs.createReadStream(filePath);
    },
    [Symbol.toStringTag]: 'File',
});

fetch(API_ENDPOINT, {
    method: 'POST',
    body,
})
    .then(res => res.text())
    .then(data => {
        console.log(data);
    });
  • Related