sending a file with axios, without FormData api - javascript

I can send a file to the server using axios and the FormData api, like that :
persist(avatar){
let data = new FormData();
data.append('avatar', avatar);
axios.post(`/api/users/${this.user.name}/avatar`, data)
.then(() => flash('Avatar uploaded!'));
}
The avatar param, passed to persist(), is a file object from a form input of type "file".
I can then grab the file on the server side.
Is it possible to do it without using FormData ? That is, to simulate the FormData work ? Basically, I'm trying to understand the extra work done by the FormData api. Maybe it's not possible using axios, and I should do that with plain XMLHttpRequest.
Of course, simply sending the file object won't work :
axios.post(`/api/users/${this.user.name}/avatar`, {avatar: avatar})
On the server side, the avatar object will be empty. I can send metadata like avatar.name, but not the whole object.

Yes, it is possible to do the encoding manually on the client. Writing a form data encoder yourself may be useful if you really want to learn every little detail about how forms work. However, for most applications, I would not recommend it. The FormData API should be used in production.
You will need to refer to RFC 7578 for how to implement the encoder.
This specification defines the multipart/form-data media type, which can be used by a wide variety of applications and transported by a wide variety of protocols as a way of returning a set of values as the result of a user filling out a form.
More resources:
Meaning of multipart/form-data
Source code for the form-data library on npm

Related

formData vs JSON when uploading images (best practices)

JSON seems to have a lot of benefits over formData for sending data to server. Some of them include sending nested data without having to manually stringfy, or making possible a simple code like below to work:
data() {
return {
form: {},
}
},
methods: {
submit() {
this.form = await this.$axios.$post('/images', this.form)
},
No matter how 'form' object is structured, i could easily send it and manage the JSON in the server. The problem with this approach seems to arrive when we need to send some uploaded files together. The most common approach seems to be sending the files as base64, but this is a bad practice since it makes file sizes bigger. Is there anyway so we can send a file appended to this JSON body without converting it to base64 or the only way would be to use formData method? Something like multipart-formdata but with JSON?
No, the json content-type cant have a file appended to it.
For APIs its better to separately do a file upload, then use the path to the file (or the disk and file name) for relating the resource to the file.
If you have to do a single request then it must be in "formData" form.
Update :
Another way is to encode the file in base64 format from the client, then decode it in the back-end (potential quality loss, have not tried this, just a suggestion)

parse form data zip file in cloud function

I am sending FormData() to a cloud function with something like this in the JavaScript frontend.
//Frontend Angular
const formData = mew FormData();
formData.append('file1', zipFile1);
formData.append('file2', zipFile2);
formData.append('name', 'MyFiles');
this.http.post(urlAPI, formData) //angularJS
//Cloud Function
function main(param) {
console.log(param);
}
The console log on the param shows the content-type is multipart/form-data is in the header.
There is also a property labeled as __ow_body. This value is a very long string of characters and digits. I am unsure what this is but I am assuming it is the files I am sending in stream/serialized format.
{
__ow_body: 'hf381fh891hv831h93n19384v938v892vn98vn2890vn29n9vn9892vn948vn2893vn2985hv98...'
}
I wanted to confirm if this is the stream data, and if so, how can I parse this?
I need to send this file, which is a zip file containing images, to a API I am using. In this documents API, the examples show sending a file in the local file system such as
someApiFunction('./myImgs.zip').then(...);
The problem lies in me sending my zip file over http network protocol and the format is very different I think from the example of reading a file in the local file system/machine. How can I deserialize/ or parse my file so that it can be recognized as a zip file containing images inside?
I tried using fs.createReadStream but it doesn't seem to be parsing it. It will just make a more structured object, but within that object, I don't see my formData and its keys such as file1 file2 and name.
Do I need to write this file some where first?
Even if it is less probable, you can try to change a bit the way of how you are posting the formData. I have found a really nice documentation about how to use formData with Angular. As it is stated there you can try to post is using something like this :
this.httpClient.post<any>(this.SERVER_URL, formData).subscribe(
(res) => console.log(res),
(err) => console.log(err)
);
This is less probable to work, but you may give it a shot.
The thing which is certain is that because of the way Cloud Functions pre-processes some requests, you can expect that some libraries not to work and I think this is the exact situation for you.
As it is stated in the Cloud Functions documentation , you need to use rawBody property of the request and 'the bus-boy' library in order to process multipart/form-data.
Here is one of the code example for handling this kind of requests.

How to push data to external JSON file (Using jQuery)?

I want to push an data to array in external local JSON file using jQuery.
So any ideas?
I have tried this:
$.getJSON('test.json', function(data) {
data.push('Something');
});
And it wont be pushed into local JSON file
You can do this in JavaScript with node.js (or a multitude of other languages/platforms) but not with a browser & jQuery.
Here's how to read and write a json file in node.js
On the other hand, users could upload a JSON file to your server, where you modify the structure and send them a modified JSON file back as a download.
Is not possible to write files using Javascript (can cause security problems through xss, csrf ..)
No, JavaScript doesn't have access to writing files as this would be a huge
security risk to say the least. If you wanted to get/store information server-
side, though, you can certainly make an Ajax call to a PHP/ASP/Python/etc.
script that can then get/store the data in the server. If you meant store data
on the client machine, this is impossible with JavaScript alone. I suspect
Flash/Java may be able to, but I am not sure.
If you are only trying to store a small amount of information for an
unreliable
period of time regarding a specific user, I think you want cookies. I am not
sure from your question what you are trying to accomplish, though.
Read/write to file using jQuery
You cannot access the files in a local client's machine. May be for development setup you can do it. But before you need to restart the browser with file-access flag set.
You can see my answer here that describes the opening browser setup with the flag set.
Then, you can use the following code to read the data.
var data = [];
var url = "/Users/Vignesh/Desktop/test.json";
var req = new XMLHttpRequest();
req.open("GET",url,true);
req.onreadystatechange=function(){
if(req.readyState === 4)
{
data = JSON.parse(req.responseText);
}
};
req.send();
To write into the file you may look into this. (Not sure it is working or not)

How to send binary data back to client using GraphQL

I have a GraphQL server, hosted on express. I want to return images to the client by sending back nodejs buffer objects. How can i config graphql server, to return bytes, instead of json? I don't wish to do this through base64, as the image are large in size.
You have to return JSON, but there's still a way. We're using GraphQL to return images stored in Blob fields in a legacy sql database. We're using sequelize, graphql-sequelize, and graphql-js.
We've defined the Blob fields to be String types in our graphql schema and so they come through in the json reply just fine.
Then we convert to a buffer before delivering, like
const imgBuffer = new Buffer.from(imgObj.data, 'ascii');
The only problem is we're now having trouble saving image data back to the database via the graphql interface. Our mutation function gives us a syntax error when it finds certain bad unicode characters in the strings, like \U0000 and whatnot (so I found your question looking for a solution to that).
There's a way, but it's complicated, and very manual, and I'm only going to give you an overview of what I've done in ApolloServer, but I think it should be enough.
First, you need to use the "Accept" header in your request to send a binary mime type, and send a matching "Content-Type" in your response. This is nessisary to be efficient, but not nessisary to work, as you'll see (with EJSON).
To serialize and deserialize respecting the headers you may need to write an express middleware, and you'll need to handle base64 encoding with a {$data: "..."} encapsulating object (as EJSON does) or just (strangely) returning null, if someone makes a request for binary data using "application/json" for their "accept" header. You'll also want to choose what binary formats that you'll support. I only use 1: "application/x-msgpack", but I hear that "application/cbor" is becoming more popular. You can use a library for EJSON, MessagePack, and CBOR to do your serialization, so this isn't as hard as it sounds.
I would then strongly recommend using the #defer on any images. See this post for more information on #defer: https://www.apollographql.com/blog/introducing-defer-in-apollo-server-f6797c4e9d6e/
I've done it. It wasn't easy, and it would be better if ApolloServer worked this way "out of the box".
It's better to send a hashed & temporary link to download it
The URL can be hashed to be non-accessible by other users.
Backend can expire the link or remove the binary file on the static server.
There might be an answer to your question by using the node module found here.

How to allow user to save reports from a REST API?

My application has to generate reports which should be available for download in XLS format. I have built a REST API using Django Rest Framework and there is an endpoint for report generation. It accepts POST requests with JSON body (report parameters, like from, to, etc., but there is also some data that represented with JSON objects) and returns JSON result. I successfully use it from Javascript, render the report as an HTML table and it works just fine.
My problem is that I need to allow users to save the report as an .xls file with a decent filename (like myawesomereport.04.12-10.12.xls. I tried JS data url approach, but as far as I understand, there is no way to set a filename if you go with that option (except setting a download attribute on an a tag, but its support is limited, so it's not the way to go). I thought that maybe I should open a new window with my API endpoint's url appropriately formed, so it outputs an XLS file, but the problem is that I do not understand if there is a way to send JSON with that request.
How should I approach this problem?
You can set the filename in the backend, by using the header Content-Disposition, so that in the frontend you can use a standard <a> tag.
In DRF, it would look like this:
response['Content-Disposition'] = 'attachment; filename={}'.format(
file_name
)

Categories