I'm trying to make sending pictures with socket.io and bumped into this error
return req.headers['transfer-encoding'] !== undefined ||
TypeError: Cannot read property 'transfer-encoding' of undefined
Before I used axios + multer, it worked perfect
So there is my code
server:
const fileSchema = new mongoose.Schema({
originalname: String,
filename: String,
path: String,
mimetype: String,
});
const File = mongoose.model("File", fileSchema);
socket.on("ROOM:FILE_UPLOAD", (formData) => {
upload.single("image")(formData, null, (err) => {
if (err) console.log(err);
else {
const originalname = formData.originalname;
const filename = formData.filename;
const path = formData.path;
const mimetype = formData.mimetype;
const file = new File({
originalname,
filename,
path,
mimetype,
});
file.save((err, image) => {
if (err) {
console.error(err);
} else {
console.log(`Image saved to MongoDB: ${image._id}`);
}
});
}
});
});
client:
const [file, setFile] = useState(null);
function handleFileSelect(event: any) {
setFile(event.target.files[0]);
}
function sendMsg(event) {
event.preventDefault();
const formData = new FormData();
formData.append("image", file);
socket.emit("ROOM:FILE_UPLOAD", formData);
}
return (
<div className="container">
<div className="row">
<form encType="multipart/form-data" onSubmit={upload}>
<input
type="file"
accept="image/*"
onChange={handleFileSelect}
name="image"
/>
<button onClick={sendMsg}>send</button>
</form>
</div>
</div>
);
where I made mistake ?
So, it was a bad idea from start to send pictures as message via sockets, it would be big server load, so I would use another algorithm similar to telegram
there is the link on docs. There is no any reason to try fix this error because the main mistake was decision how to send pictures in chat
Related
I have never had this happen before and am not sure why it's happening.
I have a component written to display PDF files in an iframe as part of a larger application. I am retrieving a BLOB stream from the server and attempting to create a URL for it to display in the iframe but it keeps giving me a cross-origin error, which I thought would not be possible since it is creating the URL out of data.
Here is my entire component:
import React, { useState, useEffect } from 'react'
import IFrameComponent from '../Elements/IFrameComponent';
const PDFPages = (props) => {
let [file, setFile] = useState(null)
let [notFound, show404]=useState(false)
useEffect(() => {
let id=props.site?.componentFile;
fetch(`${process.env.REACT_APP_HOST}/documents/GetPDF`,
{
method: 'POST'
, headers: {
'Content-Type': 'application/json'
}
, credentials: 'include'
, body: JSON.stringify({file:id})
})
.then(async response => {
let blob;
try{
blob=await response.blob(); // <--- this functions correctly
}
catch(ex){
let b64=await response.json()
blob=Buffer.from(b64.fileData,'base64')
}
//Create a Blob from the PDF Stream
//Build a URL from the file
const str=`data:application/pdf;base64,${b64.fileData}`
const url=URL.createObjectURL(blob) //<--- ERROR IS THROWN HERE
setFile(url);
})
.catch(error => {
show404(true)
});
}, []);
if(!notFound){
return <IFrameComponent src={file} title=''>
Please enable iFrames in your browser for this page to function correctly
</IFrameComponent>
}
else {
return (
<>
<h3> File {file} could not be found on server</h3>
</>
)
}
}
export default PDFPages;
For completeness here is the GetPDF function from the server which is sending the file.
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
I have done very little work in node sending files but am positive my client code is good. Where am I going wrong here?
The problem was that I was trying to be too fancy sending a BLOB or Base64 data. After investigation I rewrote
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
on the server to
router.get('/GetPDF/:fileName', async (req, res, next) => {
let fileName = req.params.fileName
fileName = `./data/documentation/${fileName.replace(/%20/g, " ")}`;
try {
let data = await dataQuery.loadFile(fileName);
res.contentType("application/pdf");
res.send(data);
}
catch (ex) {
res.send({ error: true })
}
});
Then calling it from the client using
const url = `${process.env.REACT_APP_HOST}/documents/GetPDF/${props.site.componentFile}`
as the iFrame src sends the PDF properly as expected.
This same method also solved another problem with HTML pages sent from the server not functioning correctly.
Hey guys I'm working with PouchDB and react. File uploading is working normally but when I try to convert blob into url I'm having this error in console.
"Failed to execute 'createObjectURL' on 'URL': Overload resolution
failed."
I checked the return and I'm able to retrieve all the data/images into the console and basically see the file types and so on. Anyway I heard that "createObjectURL" is deprecated or so but I followed up the tutorial provided in docs for the PouchDB. So I'm not sure now. Can someone give me any insights or help on this ? Thanks
Snippet below:
// uploading files
const uploadF = e => {
// saving chosen file
const file = e.target.files[0];
// generating random number and converting it to the string
const random_id = Math.floor(Math.random() * 10000);
const random_id_to_string = String(random_id);
console.log(file);
// insert data into local DB
db.post({
_id: random_id_to_string,
_attachments: {
fileName: {
content_type: file.type,
data: file
}
}
})
// insert data into remote db
redb.post({
_id: random_id_to_string,
_attachments: {
fileName: {
content_type: file.type,
data: file
}
}
})
// upload file to s3 bucket
S3FileUpload.uploadFile(e.target.files[0],config)
.then(data => {
console.log(data);
})
.catch(err => console.log(err))
}
// retrieve all data from db
const files = [];
db.allDocs({
include_docs: true,
attachments: true
}).then(function (result) {
return result;
})
.then(function(blob){
const url = URL.createObjectURL(blob);
console.log(url);
})
.catch(function (err) {
console.log(err);
});
return(
<section className="hero">
<nav>
<h2>Welcome</h2>
<button onClick={handleLogout}>Logout</button>
<input type="file" onChange={e => uploadF(e)} />
</nav>
</section>
);
I am trying to get my node.js backend to upload a file to AWS S3, which it got in a post request from my front-end. This is what my function looks like:
async function uploadFile(file){
var uploadParams = {Bucket: '<bucket-name>', Key: file.name, Body: file};
s3.upload (uploadParams, function (err, data) {
if (err) {
console.log("Error", err);
} if (data) {
console.log("Upload Success", data.Location);
}
});
}
When I try uploading the file this way, I get an Unsupported Body Payload Error...
I used fileStream.createReadStream() in the past to upload files saves in a directory on the server, but creating a fileStream did not work for me, since there is no path parameter to pass here.
EDIT:
The file object is created in the angular frontend of my web application. This it the relevant html code where the file is uploaded by a user:
<div class="form-group">
<label for="file">Choose File</label>
<input type="file" id="file"(change)="handleFileInput($event.target.files)">
</div>
If the event occurs, the handleFileInput(files: FileList) method in the corresponding component is called:
handleFileInput(files: FileList) {
// should result in array in case multiple files are uploaded
this.fileToUpload = files.item(0);
// actually upload the file
this.uploadFileToActivity();
// used to check whether we really received the file
console.log(this.fileToUpload);
console.log(typeof this.fileToUpload)
}
uploadFileToActivity() {
this.fileUploadService.postFile(this.fileToUpload).subscribe(data => {
// do something, if upload success
}, error => {
console.log(error);
});
}
the postFile(fileToUpload: File) method of the file-upload service is used to make the post request:
postFile(fileToUpload: File): Observable<Boolean> {
console.log(fileToUpload.name);
const endpoint = '/api/fileupload/single';
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
return this.httpClient
.post(endpoint, formData/*, { headers: yourHeadersConfig }*/)
.pipe(
map(() => { return true; }),
catchError((e) => this.handleError(e)),
);
}
Here is the the server-side code that receives the file and then calls the uploadFile(file) function:
app.post('/api/fileupload/single', async (req, res) => {
try {
if(!req.files) {
res.send({
status: false,
message: 'No file uploaded'
});
} else {
let file = req.files.fileKey;
uploadFile(file);
//send response
res.send({
status: true,
message: 'File is uploaded',
data: {
name: file.name,
mimetype: file.mimetype,
size: file.size
}
});
}
} catch (err) {
res.status(500).send(err);
}
});
Thank you very much for your help in solving this!
Best regards, Samuel
Best way is stream the file. Assuming you are. reading it from disk. You could do this
const fs = require("fs");
const aws = require("aws-sdk");
const s3Client = new aws.S3();
const Bucket = 'somebucket';
const stream = fs.createReadStream("file.pdf");
const Key = stream.path;
const response = await s3Client.upload({Bucket, Key, Body: stream}).promise();
console.log(response);
I know this has been asked before, but none of the solutions are working for me. First I tried to solve this using axios, but reading about it there seem to be a bug that won't allow me to use it for uploading files. So I'm stuck with fetch.
This is my uploading function:
export async function postStudyPlan(plan, headers) {
const options = {
method: "POST",
body: plan
}
return fetch(`${HOST}:${PORT}/study-plans/add`, options)
.then(res => {return res.json()})
.catch(err => console.log(err));
}
This is how I call it:
onStudyPlanUpload(files) {
const file = files[0];
let formData = new FormData();
formData.append("pdf", file);
formData.append("comments", "A really lit study plan!");
formData.append("approved", true);
formData.append("uploaded_by", "Name");
formData.append("date_uploaded", "2012-02-1");
formData.append("university", "australian_national_university");
let plan = {
"pdf": file,
"comments": "A really lit study plan!",
"approved": true,
"uploaded_by": "Name",
"date_uploaded": Date.now(),
"university": "australian_national_university"
}
postStudyPlan(formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
I know that file is in fact a file. Whenever I change "pdf" to a normal string, everything works fine. But when I use a File object, I recieve nothing to my backend, just an empty object. What am I missing here? I feel like my solution is basically identical to every other solution I've found online.
Edit:
Also tried using FormData and adding headers: {"Content-Type": "application/x-www-form-urlencoded"} to options. Same result.
Edit 2
I'm beginning to think my backend might be the problem! This is my backend, and I'm actually getting some outputs for the "data" event. Not sure how to process it...
router.route("/add").post((req, res) => {
req.on("data", function(data) {
console.log("got data: " + data.length);
console.log("the Data: ?" )
// let t = new Test(data);
// t.save()
// .then(res => console.log(res))
})
req.on("end", function(d) {
console.log("ending!");
})
req.on("error", function(e){
console.log("ERROR: " + e);
})
});
You should use FormData with 'Content-Type': 'application/x-www-form-urlencoded' as fetch header.
I want you to try a simple approach. Instead of appending the file into FormData, create an instance of an actual form.
onStudyPlanUpload = (event) => {
event.preventDefault();
const formData = new FormData(event.target);
postStudyPlan(formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
HTML
<form onSubmit={this.onStudyPlanUpload} encType="multipart/form-data" ref={el => this.form = el}>
<input type="file" name="pdf" onChange={() => { this.form.dispatch(new Event('submit'))}/>
<input type="hidden" name="comments" value="A really lit study plan!" />
<input type="hidden" name="approved" value=true />
<input type="hidden" name="uploaded_by" value="Name"/>
<input type="hidden" name="date_uploaded" value="2012-02-1"/>
<input type="hidden" name="university" value="australian_national_university"/>
</form>
While changing the file input, It will trigger the form submit (onStudyPlanUpload).
Hope this will work!
I'll answer my own question. If anyone else comes across this problem, it is the BACKEND that's faulty. This is my final solution using busboy to handle incoming form data. I didn't change anything on my server, but my router had to be updated. Here is my router, taking care of POST requests:
const express = require("express");
const mongoose = require("mongoose");
require('./../../models/Test');
const path = require("path");
const fs = require("fs");
const router = express.Router();
const Busboy = require("busboy");
router.route("/add").post((req, res, next) => {
let busboy = new Busboy({headers: req.headers});
// A field was recieved
busboy.on('field', function (fieldname, val, valTruncated, keyTruncated) {
if (req.body.hasOwnProperty(fieldname)) { // Handle arrays
if (Array.isArray(req.body[fieldname])) {
req.body[fieldname].push(val);
} else {
req.body[fieldname] = [req.body[fieldname], val];
}
} else { // Else, add field and value to body
req.body[fieldname] = val;
console.log(req.body);
}
});
// A file was recieved
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
console.log("File incoming: " + filename);
var saveTo = path.join('.', filename);
console.log('Uploading: ' + saveTo);
file.pipe(fs.createWriteStream(saveTo));
});
// We're done here boys!
busboy.on('finish', function () {
console.log('Upload complete');
res.end("That's all folks!");
});
return req.pipe(busboy);
});
module.exports = router;
Finally, my finished onStydyPlanUpload() function!
onStudyPlanUpload(files) {
const file = files[0];
let formData = new FormData();
formData.append("pdf", file, file.name);
formData.append("comments", "A really lit study plan!");
formData.append("approved", true);
formData.append("uploaded_by", "Melker's mamma");
formData.append("date_uploaded", new Date());
formData.append("university", "australian_national_university");
const HOST = "http://localhost";
const PORT = 4000;
axios.post(`${HOST}:${PORT}/test/add`, formData)
.then(res => console.log(res))
.catch(err => console.log(err))
}
Got help from: https://gist.github.com/shobhitg/5b367f01b6daf46a0287
In the application I'm currently working on, there are a couple of file forms that are submitted via superagent to an Express API endpoint. For example, image data is posted like so:
handleSubmit: function(evt) {
var imageData = new FormData();
if ( this.state.image ) {
imageData.append('image', this.state.image);
AwsAPI.uploadImage(imageData, 'user', user.id).then(function(uploadedImage) {
console.log('image uploaded:', uploadedImage);
}).catch(function(err) {
this.setState({ error: err });
}.bind(this));
}
}
and this.state.image is set like this from a file input:
updateImage: function(evt) {
this.setState({
image: evt.target.files[0]
}, function() {
console.log('image:', this.state.image);
});
},
AWSAPI.uploadImage looks like this:
uploadImage: function(imageData, type, id) {
var deferred = when.defer();
request.put(APIUtils.API_ROOT + 'upload/' + type + '/' + id)
.type('form')
.send(imageData)
.end(function(res) {
if ( !res.ok ) {
deferred.reject(res.text);
} else {
deferred.resolve(APIUtils.normalizeResponse(res));
}
});
return deferred.promise;
}
And lastly, the file receiving endpoint looks like this:
exports.upload = function(req, res) {
req.pipe(req.busboy);
req.busboy.on('file', function(fieldname, file) {
console.log('file:', fieldname, file);
res.status(200).send('Got a file!');
});
};
Currently, the receiving endpoint's on('file') function never gets called and so nothing happens. Previously, I've tried similar approaches with multer instead of Busboy with no more success (req.body contained the decoded image file, req.files was empty).
Am I missing something here? What is the best approach to upload files from a (ReactJS) Javascript app to an Express API endpoint?
I think superAgent is setting the wrong content-type of application/x-form-www-encoded instead of multipart/form-data you can fix this by using the attach method like so:
request.put(APIUtils.API_ROOT + 'upload/' + type + '/' + id)
.attach("image-file", this.state.image, this.state.image.name)
.end(function(res){
console.log(res);
});
for more information about the attach method, read the documentation here: http://visionmedia.github.io/superagent/#multipart-requests
since this involves a nodejs server script I decided to make a GitHub repo instead of a fiddle: https://github.com/furqanZafar/reactjs-image-upload
From experience, uploading a file using ajax works when you use FormData, however the file must be the only form field / data. If you try and combine it with other data (like username, password or pretty much anything at all) it does not work. (Possibly there are work arounds to get around that issue, but I am not aware of any)
If you need to send the username/password you should be sending those as headers if you can instead.
Another approach I took was first do the user registration with the normal data, then on success I upload the file with the FormData separately as an update.
The react file upload iamges component:
class ImageUpload extends React.Component {
constructor(props) {
super(props);
this.state = {file: '',imagePreviewUrl: ''};
}
_handleSubmit(e) {
e.preventDefault();
// this.uploadImage()
// TODO: do something with -> this.state.file
console.log('handle uploading-', this.state.file); }
_handleImageChange(e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file) }
// XHR/Ajax file upload uploadImage(imageFile) {
return new Promise((resolve, reject) => {
let imageFormData = new FormData();
imageFormData.append('imageFile', imageFile);
var xhr = new XMLHttpRequest();
xhr.open('post', '/upload', true);
xhr.onload = function () {
if (this.status == 200) {
resolve(this.response);
} else {
reject(this.statusText);
}
};
xhr.send(imageFormData);
}); }
render() {
let {imagePreviewUrl} = this.state;
let $imagePreview = null;
if (imagePreviewUrl) {
$imagePreview = (<img src={imagePreviewUrl} />);
} else {
$imagePreview = (<div className="previewText">Please select an Image for Preview</div>);
}
return (
<div className="previewComponent">
<form onSubmit={(e)=>this._handleSubmit(e)}>
<input className="fileInput" type="file" onChange={(e)=>this._handleImageChange(e)} />
<button className="submitButton" type="submit" onClick={(e)=>this._handleSubmit(e)}>Upload Image</button>
</form>
<div className="imgPreview">
{$imagePreview}
</div>
</div>
) } } React.render(<ImageUpload/>, document.getElementById("mainApp"));
The Server Side Image Save and Copy:
Along with express You needed to npm install 'multiparty'. This example uses multiparty to parse the form data and extract the image file information. Then 'fs' to copy the temporarily upload image to a more permanent location.
let multiparty = require('multiparty');
let fs = require('fs');
function saveImage(req, res) {
let form = new multiparty.Form();
form.parse(req, (err, fields, files) => {
let {path: tempPath, originalFilename} = files.imageFile[0];
let newPath = "./images/" + originalFilename;
fs.readFile(tempPath, (err, data) => {
// make copy of image to new location
fs.writeFile(newPath, data, (err) => {
// delete temp image
fs.unlink(tempPath, () => {
res.send("File uploaded to: " + newPath);
});
});
});
})
}