How can I upload a file from my angular controller. I am doing something like, On ng-click I am calling upload_file() function which is declared inside my controller. And I want to use something like this
$http.post("url", data).success().error();
url is of node upload service. It's working fine when I use like . But without using action there, I want to upload it from my function.
But I am not getting how to attach the file selected to the data here. I want to send some data along with file. Can I upload it in the way I am trying? please help me...
You can use angular-file-upload library:
Basically you need to $http.post like this:
$http({
method: 'POST',
url: config.url,
headers: { 'Content-Type': false },
transformRequest: function (data) {
var formData = new FormData();
formData.append('file', myFile);
for (key in myData) {
formData.append(key, myData[key]);
}
return formData;
}
}).success().error().progress()
The library supports IE with flash polyfill which normally doesn't support FormData so you cannot get the file object from the <input file...>.
Related
I have a Node.js (16.13.1) REST API using Express and one of my endpoints receives one or more uploaded files. The client (web app) uses FormData into which the files are appended. Once they're submitted to my API, the code there uses multer to grab the files from the request object.
Now I'm having trouble trying to send those same files to another API. multer attaches the files to req.files and each file object in that array has several properties one of which is buffer. I tried using the stream package's Duplex object to convert this buffer to a stream so that I could append the file to another FormData object, but when the server the second API is running on receives the request, I get an error from the web server saying that "a potentially dangerous request.form value was detected from the client.".
Any suggestions?
I am working on a nest project I was also facing this issue did some research and found that we need to create a Readable from the Buffer of that file and it's working for me.
// Controller
#UseInterceptors(FileInterceptor('file'))
async uploadFile(#UploadedFile() file: Express.Multer.File) {
return this.apiservice.upload(file);
}
// Service
uploadFile(file: Express.Multer.File) {
const readstream = Readable.from(file.buffer)
console.log(readstream)
const form = new FormData();
form.append('file', file, { filename: extra.filename });
const url = `api_endpoint`;
const config: AxiosRequestConfig = {
headers: {
'Content-Type': 'multipart/form-data'
},
};
return axios.post(url, form, config);
}
I did the following request with JS and it worked. Now trying to adapt in for react and getting an empty object being posted. Below just bits of code, but basically I am generating pdf and aiming to send it to backend:
const doc = new jsPDF();
const blob = doc.output('blob');
const email = formData.emailRequired;
const formDataToSend = new FormData();
formDataToSend.append('email', email);
formDataToSend.append('file', blob);
console.log(formDataToSend)
fetch('http://localhost:8080/api/contract',
{
method: 'POST',
data: formDataToSend,
processData: false,
contentType: false,
});
Unfortunatelly on submit I get FormData {} - so it's an empty object. Why?
FormData is not an empty object, it is displayed like this in the console.
If you want to display all entries, then use this approach
console.log([...formDataToSend.entries()])
Related question How to inspect FormData?
I retrieve this URL from using some previous logic - https://bots.rocket.chat/file-upload/ZSzGtWdXyP8DZwrQ9/Audio%20record.mp3
Now I need to download it for further processing, but I tried using the download, axios.request, and other packages available. My best guess for them failing is that the actual URL of the audio is different then what I retrieved.
Something like (It changes after a while) -https://s3.amazonaws.com/uploads.use1.cloud.rocket.chat/KqCQiHfeFaKdSEGvy/uploads/GENERAL/XmYemgXfudSzBQstR/ZSzGtWdXyP8DZwrQ9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAILPK6SHTK5RJZLHQ%2F20190518%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20190518T115900Z&X-Amz-Expires=120&X-Amz-Signature=8d793d14fbb33d78b084a5ed153106ba19440feb7bf05525aeed530b5a0907f9&X-Amz-SignedHeaders=host
Also, I'm able to download the file using the same code when I provide it with the end URL (the longer one).
So need to figure out how to reach that URL in node.
Also to access the first URL you need to make an account on https://bots.rocket.chat
This is the code I have currently. The url passed to getFile function is the is a relative path which I append to the prefix url to form the above mentioned shorter URL, and call axios and this doesn't work.
But when I replace the url with a long url which is similar to the longer one posted above the code works just fine.
async function getFile(url) {
var fs = require('fs');
return await axios.request({
responseType: 'arraybuffer',
followAllRedirects: true,
url: `https://bots.rocket.chat${url}`,
method: 'get',
headers: {
'Content-Type': 'audio/mpeg',
},
}).then((result) => {
const outputFilename = 'file2.mp3';
fs.writeFileSync(outputFilename, result.data);
return outputFilename;
})
.catch(err => console.log(err));
;
}
Hey since google drive was changing their library I´am not able to upload anymore files bigger than 5MB with the basic upload drive.files.create. The docs told me that I have to choose resumable uploads instead. But google drive didn´t provide any sample code and aswell I can´t find anything on google.
Maybe it´s important to know that I can upload files smaller than 5MB with the drive.files.create
So there is no problem with the auth.
https://developers.google.com/drive/v3/web/resumable-upload
I wrote this POST request(Also not working with PUT):
var fs = require('fs')
var request = require('request')
var file = 'C:\\test\\sample.container'
var uploadUrl = 'https://www.googleapis.com/drive/v3/files?uploadType=resumable'
var stats = fs.statSync(file)
var fileSizeInBytes = stats["size"]
fs.readFile(file, function read(e, f) {
if (e) {
console.log(e)
return;
}
request.post({
url: uploadUrl,
headers: {
'Authorization': 'xxxxxxxxxxxxxxxxxxxxxxx',
'Content-Length': fileSizeInBytes,
'Content-Type': 'application/octet-stream'
},
body: f,
}, function(e, r, b) {
if (e) {
console.log(e)
return;
}
console.log(`
Response: ${ JSON.stringify(r) }
Body: ${ b }
`)
});
});
but I get as body result:
<HTML>
<HEAD>
<TITLE>Request Entity Too Large</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Request Entity Too Large</H1>
<H2>Error 413</H2>
</BODY>
</HTML>
If I would use as request url instead:
https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable
I get aswell an similiar message as body result:
Request is too large.
So anybody has a working code for uploading files with the resumable upload or maybe again with the basic upload? Or is there another way for uploading big files? I´am open for alternatives! Thank you
In other api clients (e.g. the Python one), resumable uploads are created by altering the MediaFileUpload constructor with the parameter resumable=True. The node.js api client is only in alpha, so it may not have built-in support for resumable uploads. You can try feeding drive a stream, or simply extending that example media parameter, e.g.
media: {
mimeType: 'some mimetype',
body: 'some body',
resumable: true
}
If stream and the above resumable don't work, then you won't be able to use the node.js client library to do resumable uploads, and will have to use the REST API directly.
The problem itself was related to the googleapi library for node.js. With v27.0.0 the basic upload was working again for big files aswell. Related to:
https://github.com/google/google-api-nodejs-client/issues/1038
This is not an answer for how to resumable upload with node.js so you may keep this topic open until somebody post a sample code for resumable upload because even with v27 my POST request is still not working. Maybe you watch the github link from above because I asked there aswell for an sample code.
However my problem was only that I was not able to upload bigger files than 5MB. But this problem is now gone for me :)!
I have the following (simplified for example) angular directive which creates a dropzone
directives.directive('dropzone', ['dropZoneFactory', function(dropZoneFactory){
'use strict';
return {
restrict: 'C',
link : function(scope, element, attrs){
new Dropzone('#'+attrs.id, {url: attrs.url});
var myDropZone = Dropzone.forElement('#'+attrs.id);
myDropZone.on('sending', function(file, xhr, formData){
//this gets triggered
console.log('sending');
formData.userName='bob';
});
}
}
}]);
As you can see the the sending event handler I'm trying to send the username ("bob") along with the uploaded file. However, I can't seem to retrieve it in my route middleware as req.params comes back as an empty array (I've also tried req.body).
My node route
{
path: '/uploads',
httpMethod: 'POST',
middleware: [express.bodyParser({ keepExtensions: true, uploadDir: 'uploads'}),function(request,response){
// comes back as []
console.log(request.params);
//this sees the files fine
console.log(request.files);
response.end("upload complete");
}]
}
Here is what the docs say on the sending event
Called just before each file is sent. Gets the xhr object and the formData objects as second and third parameters, so you can modify them (for example to add a CSRF token) or add additional data.
EDIT
I dropped the programmatic approach for now. I have two forms submitting to the same endpoint, a regular one with just post and a dropzone one. Both work, so I don't think it's an issue with the endpoint rather with how I handle the 'sending' event.
//Receives the POST var just fine
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz")
input(type="hidden", name="additionaldata", value="1")
input(type="submit")
//With this one I can get the POST var
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz2", class="dropzone")
input(type="hidden", name="additionaldata", value="1")
OK, I've actually figured it out, thanks to Using Dropzone.js to upload after new user creation, send headers
The sending event:
myDropZone.on('sending', function(file, xhr, formData){
formData.append('userName', 'bob');
});
As opposed to formData.userName = 'bob' which doesn't work for some reason.
I would like to add to NicolasMoise's answer.
As a beginner in webdev I got stuck on how to obtain an instance of Dropzone. I wanted to retrieve an instance of Dropzone that had been generated by the autodiscovery feature. But it turns out that the easiest way to do this is to manually add a Dropzone instance after first telling Dropzone not to auto-discover.
<input id="pathInput"/>
<div id="uploadForm" class="dropzone"/>
<script>
$(document).ready(function(){
Dropzone.autoDiscover = false;
var dZone = new Dropzone("div#uploadForm", {url: "/api/uploads"});
dZone.on("sending", function(file, xhr, data){
data.append("uploadFolder", $("#pathInput")[0].value);
});
});
</script>
Serverside the data will be in request.body.uploadFolder
Nicolas answer is one possible solution to the problem. It is especially useful if you need to alter the file object prior to sending.
An alternative is to use the params option:
var myDropzone = new Dropzone("div#myId",
{ url: "/file/post", params: { 'param_1': 1 }});
cf. the documention
For those that are using thatisuday/ng-dropzone the callback methods are done as such:
<ng-dropzone class="dropzone" options="dzOptions" callbacks="dzCallbacks" methods="dzMethods"></ng-dropzone>
In a controller:
$scope.dzCallbacks = {
sending: function(file, xhr, form) {
console.log('custom sending', arguments);
form.append('a', 'b');
}
};