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);
});