I'm developing a simple Javascript application where the user has some images (stored in my machine) and he is able to annotate them and then save the annotations as a JSON file.
The application is very light and simple and it is not an app server.
However, I need to save those JSON files to the machine that will be behaving as the server.
Since I cannot use Javascript for IO, is there any easy and simple way to save those files without having to implement an app server?
I used Blob to download the files.
function project_save_confirmed(input) {
if ( input.project_name.value !== _onco_settings.project.name ) {
project_set_name(input.project_name.value);
}
// onco project
var _onco_project = { '_onco_settings': _onco_settings,
'_onco_img_metadata': _onco_img_metadata,
'_onco_attributes': _onco_attributes };
var filename = input.project_name.value + '.json';
var data_blob = new Blob( [JSON.stringify(_onco_project)],
{type: 'text/json;charset=utf-8'});
save_data_to_local_file(data_blob, filename);
user_input_default_cancel_handler();
}
function save_data_to_local_file(data, filename) {
var a = document.createElement('a');
a.href = URL.createObjectURL(data);
a.download = filename;
a.click();
}
Any suggestion?
Kind regards!
Copy paste from: Download JSON object as a file from browser
function downloadObjectAsJson(exportObj, exportName){
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
var downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
This I believe accomplishes what you want, just makes sure that the proper headers are set, push it to an <a> tag, then click() it
You can do this in php:
<?php
//notice this will put WHATEVER is in json into file
$filename="config.json";
if (isset($_POST["json"])) {
file_put_contents($filename,$_POST["json"]);
}
?>
then for the JS side:
var fd=new FormData();
fd.append("json", JSON.stringify(_onco_project));
fetch("https://url.com",{method:"POST",body:fd})
Explanation: JS makes a new formdata, and sets "json" to the stringified json, and sends it off to the server. The php server takes this, and puts it directly into $filename. Make sure data is safe before putting it to file, as it will take whatever it is given and put it into your file!
Related
I'm trying to upload a file, but i want to normalize it's name fisrt, it works on other browsers, but in IE11, i searched and i found out that this method (normalize) is not supported, so i'm using polyfill unorm. so normalizing works fine now, but we can't change the fileName directly, we need to create a new file. But we can't use new File because it's not supported too. So I used new Blob, but the problem is that i don't get the filename on the server side, it's always blob.
The code for other browsers :
var fileName = file.name.normalize('NFD').replace(/[\u0300-\u036f]/g, "");
var newFile = new File([file], fileName, { type: file.type });
newFile.label = 'FICHIER';
The code for IE11
fileName = unorm.nfd(file.name);
newFile = new Blob([file], { type: file.type });
newFile.label = 'Fichier';
newFile.name= fileName;
To generate the request to the server, i use formdata :
fd = new FormData();
fd.append("id", param);
fd.append(file.label || "uploadedFile", file, file[paramName]);
Can you tell me what should i do to get the filename or if there is another way to do this.
The Blob object doesn't contain the name property, so, we can't change name via the Blob object.
After getting the file data, I suggest you could append a new parameter to log the new file name, then, when submits the form or save the uploaded file, you could send the file data and the new file name to the server.
Besides, here is another thread about upload file using FormData, please refer to it:
Angular File Upload
File Upload using AngularJS
So in a client-side HTML page, a user selects a file and uploads it to the JavaScript code. JavaScript parses the file and sends it to the server and back to everyone else who is on the site. Then every client makes a blob download link for the file. It's easy when I can send the file to server and back like this.
But now, I want to make that file available for future users of the site without saving it to a location. This is in a chat program, so I've been sending messages from users as strings to a database. I'd like to create a program to send the aforementioned File object to the shortest string possible and then recreate the file (including all metadata) at another client from this string.
What is the standard way to convert a Blob to a string and back again without losing anything? If there's multiple ways, what results in the shortest string?
I found the answer to my question, I had to modify some other answers from SO questions that only sorta applied to my question. Here's what I found:
This is on the uploading-client, in the function called when a file is uploaded:
let inp = document.getElementById("file_input");
let reader = new FileReader();
reader.onload = function(){
send_off_to_other_clients(reader.result);
}
reader.readAsBinaryString(inp.files[0]);
On the other clients:
<script>
function get_blob_from_string (string, type, name) {
let array = new Uint8Array(string.length);
for (let i = 0; i < string.length; i++){
array[i] = string.charCodeAt(i);
}
let end_file = new Blob([array], {type: type, name: name});
let a = document.createElement("a");
a.href = URL.createObjectURL(end_file);
a.download = name;
a.target = "_blank";
a.click();
}
</script>
end_file is the returned-to-blob version, and then I create an anchor tag to download it. Probably isn't "proper" but it works.
I have a javascript object that contains some information.
I want to convert this into JSON and download it as a .json file.
Seems like I can just to JSON.stringify(obj) to convert it into JSON
but how do I actually download it as a .json file?
I'm not sure this is a React-specific issue if you're just looking to download data via JavaScript, but here's a snippet I use that creates a link to download the data content, virtually clicking the element, and finally removing it from the DOM. It should support both modern browsers and older IEs:
private exportToJson(objectData: SomeObject) {
let filename = "export.json";
let contentType = "application/json;charset=utf-8;";
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
var blob = new Blob([decodeURIComponent(encodeURI(JSON.stringify(objectData)))], { type: contentType });
navigator.msSaveOrOpenBlob(blob, filename);
} else {
var a = document.createElement('a');
a.download = filename;
a.href = 'data:' + contentType + ',' + encodeURIComponent(JSON.stringify(objectData));
a.target = '_blank';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
It's also worth noting that there are a number of ways to approach this as cited in this SO question.
For those arriving here and searching for an easier solution:
<a
href={`data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(YOURJSON)
)}`}
download="filename.json"
>
{`Download Json`}
</a>
You won't be able to create a file directly on your computer's file system as this would be a huge security risk. You cannot do this from a web page using JavaScript.
You could write a server side service to post your state to and it creates a file - you may then download the file or be content with where your server side stores it.
Another way via inMemory Create a file in memory for user to download, not through server
I have an API-Server who responds to requests like this:
http://localhost:8080/slim3/public/api/v1/files/Test1.jpg
http://localhost:8080/slim3/public/api/v1/files/Test2.txt
...
If I put such URL into my browser I can get the download prompt. Now I'm struggeling to process the download of a file via jQuery / Ajax.
Every thread I found here on Stackoverflow tells me to send back the actual download url and open it via window.location. I don't understand how this is possible
when my server already has the file downloaded for me and I just need to "grab" it somehow on the client-side?
It is clear to me that I can't force the download dialog via jQuery / Javascript. I read this in multiple threads here. But the same threads don't tell me
how I can get the direct download url. Or do I mix things up here unfortunately?
Here is what I have:
Client (jQuery)
$(document).ready(function(){
$(document).on('click', '#file', function(e){
e.preventDefault();
var filename = $(this).data('url');
$.ajax({
type : "GET",
cache: false,
url : "http://localhost:8080/slim3/public/api/v1/files/" + filename,
success : function(data) {
console.log(data) // the console writes nothing
//window.location = "data:application/octet-stream," + encodeURIComponent(data); // not working
//var downloadUrl = data.url; // not working
//window.location = downloadUrl; // // not working
},
error : function(data) {}
});
});
});
Server (PHP)
public function show($request, $response, $args)
{
$file = 'C:\xampp\htdocs\slim3\storage\Test1.jpg';
$res = $response->withHeader('Content-Description', 'File Transfer')
->withHeader('Content-Type', 'application/octet-stream')
->withHeader('Content-Disposition', 'attachment;filename="'.basename($file).'"')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate')
->withHeader('Pragma', 'public')
->withHeader('Content-Length', filesize($file));
readfile($file);
return $res;
}
Solution:
Rob pointed me in the right direction. I actually don't need to do an GET Ajax request. So the final jQuery function looks exacty like this and works:
$(document).on('click', '#file', function(e){
e.preventDefault();
var filename = $(this).data('url');
window.location = "http://localhost:80/slimmi/public/api/v1/files/" + filename;
});
In Client,filename variable will be wrong. It should be Test1.jpgor Test2.txt. I think that $(this).data('url'); returns the current url instead of Test1.jpgor Test2.txtnames. Do you try to substract the file name by using:
var url = $(this).data('url');
var filename = url.substring(url.lastIndexOf("/") + 1, url.length);
Your server just sends back the actual file requested by name in the URL right?
It looks to me like you just need to replace all of the ajax code with
document.location = "http://localhost:8080/slim3/public/api/v1/files/" + filename;
The headers that you set in the PHP will determine whether the browser shows the save dialog or attempts to display the file - those look right.
What you could do if the files are generated on demand is have PHP encode your file in Base64 - like this, setting the appropriate type - and return that to the client. Convert the Base64 to a Blob - you can put Base64 in an anchor's href but IE has a prohibitively small URI size - then create a URL object from that Blob. Among other things this ensures that the data is URL safe. Finally, create an "invisible" anchor tab and click it.
$.ajax({
type: "GET",
url: target,
success: function (response) {
// create a download anchor tag
var downloadLink = document.createElement('a');
downloadLink.target = '_blank';
downloadLink.download = 'your-file-name-here';
// convert Base64 to Blob - don't forget to set content type!
var blob = b64toBlob(response, [file type here]);
// create an object URL from the Blob
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// set object URL as the anchor's href
downloadLink.href = downloadUrl;
// append the anchor to document body
document.body.appendChild(downloadLink);
// fire a click event on the anchor
downloadLink.click();
// cleanup: remove element and revoke object URL
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
}
});
Convert the Base64 to Blob like this - source.
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
This is what I use to download PDF's generated on demand by our Django server and she seems to run pretty darn well.
Addendum
The reason why our website does it this way instead of just returning the file name for a subsequent call is because it's a bit easier on the server I/O. The solution that was chosen means that the requested file has to exist somewhere on the server - most likely on disk. (One might be able to keep the generated file in memory using PHP's tmpfile() but my knowledge of PHP is limited so I do not know how you would keep that file around between HTTP calls).
My project makes big honking PDF's - possibly hundreds of pages. I really, really don't want to have to make an actual file object out of this data, save it to disk, and then almost immediately read it back off the disk (I am aware that that isn't exactly how the server is doing it, but anyway you slice it it's doing more work than necessary). The server has the PDF made, it's in memory, why not just ... give it back to the client?
Returning files like this means that one doesn't need to do any extra clean up work - once the Base64 has left the building, that's it. There's no file on disk so there's nothing that has to be dealt with later (good or bad depending on your needs).
I have a web application for downloading files. Everything works fine except when I want to download a file more than 1GB .
This is my java code:
InputStream in = new FileInputStream(new File(folderFile.getAbsolutePath()));
org.apache.commons.io.IOUtils.copy(in, response.getOutputStream());
response.flushBuffer();
in.close();
HTTP request :
$http({
method:'get',
url:this.apiDownloadFileUrl,
responseType:'arraybuffer',
cache: false
});
and here is client side: I got data successfully on client, but when I make it Blob , if the data size was more than 500MB , nothing happened and it wasn't downloaded. Also, I can download 300MB ...
How can I check if it is a memory problem, or a server problem? ... When I download from gmail , I can download more than 1GB .
.success(function(databack) {
var file = new Blob([ databack ], {
type : 'application/csv'
});
var fileURL = window.URL.createObjectURL(file);
var a = document.createElement('a');
a.href = fileURL;
a.target = '_blank';
a.download = data;
document.body.appendChild(a);
a.click();
Have you tried using the copyLarge() methods from IOUtils? For the copy() methods the JavaDoc says:
"For large streams use the copyLarge(InputStream, OutputStream) method."
You should check the response message first, and decide which side fire the problem.
As my experience, you should check whether the file was cached by the browser rather than any problems~