Rename file saved to the file system with angular 2 - file

I want to save a blob downloaded from a server onto the file system, so i use
window.open( window.URL.createObjectURL(myBlob));
to save it. But when i do that, then the saved file gets a completly jumbled random name.
Is there any way to give the saved file my own name?
EDIT:
Server side looks like this (Spring Data REST controller):
#RequestMapping(value = "/downloadFile/{fileName}", method = RequestMethod.GET)
public void downloadFile(#PathVariable(value="fileName") String fileName, HttpServletResponse response) throws IOException {
File file = getMyFile(fileName)
response.addHeader("Content-Disposition", "attachment; filename="+file.getName());
response.setContentType(Files.probeContentType(file.toPath()));
response.setContentLengthLong(file.length());
response.getOutputStream().write(FileUtils.readFileToByteArray(file));
}

I've been searching for a similar answer myself and found several possibilities to save a downloaded file.
In your case you can use the download attribute of the <a> element:
. You can dynamically add this element when for instance a button is clicked, and programmatically perform the click event.
var a = document.createElement("a"),
fileURL = URL.createObjectURL(blob);
a.style = "display:none";
a.href = fileURL;
a.download = fileName;
window.document.body.appendChild(a);
a.click();
window.document.body.removeChild(a);
URL.revokeObjectURL(fileURL);
The download atrribute only has recent support: http://www.w3schools.com/tags/att_a_download.asp
For older versions of IE, use
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveOrOpenBlob(blob, fileName);
}

Related

How to save Base64 String to file and view it using Flutter

I need to download and view file (if possible, for Image, PDF, etc) using Flutter.
My problem is, the file that I want to download is Base64 String.
How can I achieve that using Flutter??
Following is the code snippet to decode base64 string and save it as a file on the local device. please note in terms of viewing files, image and pdf would require different libraries.
Future<String> _createFileFromString() async {
final encodedStr = "put base64 encoded string here";
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File(
"$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + ".pdf");
await file.writeAsBytes(bytes);
return file.path;
}
Google Chrome does not let open tab with Url base64 encode, like "data:application/pdf;base64,JVBERi0xLjQKJeLj..." with javascript, so you can not open url with url_launcher, but you can download file.
Here is my code:
import 'dart:html' as html;
...
Future<void> downloadFile() {
final urlString = "data:application/pdf;base64,JVBERi0xLjQKJeLj...";
html.AnchorElement anchorElement = html.AnchorElement(href:urlString);
anchorElement.download = urlString;
anchorElement.click();
}

Download xlsx file passed from nodejs to frontend angularjs

I have a JSON array of objects that is a result of a function in nodejs. I use json2xls to convert that to an excel file, and it downloads to the server (not in a public folder, and is formatted correctly in Excel).
I would like to send a response to the frontend with the json results (to display as a preview) and show a button they can click to download the xlsx file OR display the JSON results and automatically download the file.
But I can't get it, and I've tried so many things I'm going crazy.
My controller code (the part that creates the xls file):
var xls = json2xls(results,{});
var today = (new Date()).toDateString('yyyy-mm-dd');
var str = today.replace(/\s/g, '');
var fileName = "RumbleExport_"+ str +".xlsx";
var file = fs.writeFileSync(fileName,xls,'binary');
res.download('/home/ubuntu/workspace/'+file);
The frontend controller:
vm.exportData = function(day, event, division) {
console.log('Export registrations button pressed.', vm.export);
//send the search parameters to the backend to run checks
$http.post('/api/exportData', vm.export).then(function(response){
vm.results = response.data;
console.log("Results",response);
vm.exportMessage = "Found " + vm.results.length + " registrations.";
})
.catch(function(error){
vm.exportError = error.data;
});
};
The view:
//display a button to download the export file
<a target="_self" file="{{vm.results}}" download="{{vm.results}}">Download Export File</a>
Someone please put me out of my misery. All the classes I've taken and none have covered this.
I FINALLY got it! And since I searched forever trying to make something work, I'll share the answer:
On the backend:
//save the file to the public/exports folder
var file = fs.writeFileSync('./public/exports/'+fileName,xls,'binary');
//send the results to the frontend
res.json(200).json({results:results, fileName: fileName});
On the frontend, use HTML to download a link to the file:
<a href="exports/{{fileName}}" download>Save File</a>

AngularJS GET receives empty reply in Chrome but not in Fiddler

I'm implementing file download using AngularJS and WCF. My back-end is a .NET project hosted in IIS. The file is serialized as an array of bytes and then on the client side I utilize the File API to save the content.
To simplify the problem, back-end is like:
[WebInvoke(Method = "GET", UriTemplate = "FileService?path={path}")]
[OperationContract]
public byte[] DownloadFileBaseOnPath(string path)
{
using (var memoryStream = new MemoryStream())
{
var fileStream = File.OpenRead(path);
fileStream.CopyTo(memoryStream);
fileStream.Close();
WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = "attachment; filename=\"Whatever\"";
WebOperationContext.Current.OutgoingResponse.ContentType = "application/octet-stream"; // treat all files as binary file
return memoryStream.ToArray();
}
}
And on client side, it just sends a GET request to get those bytes, converts in into a blob and save it.
function sendGetReq(url, config) {
return $http.get(url, config).then(function(response) {
return response.data;
});
}
Save the file then:
function SaveFile(url) {
var downloadRequest = sendGetReq(url);
downloadRequest.then(function(data){
var aLink = document.createElement('a');
var byteArray = new Uint8Array(data);
var blob = new Blob([byteArray], { type: 'application/octet-stream'});
var downloadUrl = URL.createObjectURL(blob);
aLink.setAttribute('href', downloadUrl);
aLink.setAttribute('download', fileNameDoesNotMatter);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', false, false);
aLink.dispatchEvent(event);
}
else {
aLink.click();
}
setTimeout(function () {
URL.revokeObjectURL(downloadUrl);
}, 1000); // cleanup
});
}
This approach works fine with small files. I could successfully download files up to 64MB. But when I try to download a file larger than 64MB, the response.body is empty in Chrome. I also used Fiddler to capture the traffic. According to Fiddler, Back-end has successfully serialized the byte array and returned it. Please refer to the screenshot below.
In this example, I was trying to download a 70MB file:
And the response.data is empty:
Any idea why this is empty for file over 70MB? Though the response itself is more than 200MB, I do have enough memory for that.
Regarding to the WCF back-end, I know I should use Stream Mode when it comes to large files. But the typical use of my application is to download files less than 10MB. So I hope to figure this out first.
Thanks
Answer my own question.
Honestly I don't know what's going wrong. The issue still persists if I transfer it as a byte array. I eventually gave up this approach by returning a stream instead. Then on the client side, adding the following configuration
{responseType : blob}
and save it as a blob.

File upload using AngularJS -> .NET Web Api 2 -> SQL server

I'm looking for a solution where I can upload any file to SQL server from an AngularJS frontend to .Net Web Api 2 and straight to SQL Server Database. I've done some research and for angularjs i'm mainly looking at ng-file-upload. my problem is most of the solutions that i've looked at saves the file into a temp folder. I'm not sure if it's possible but I want it straight to an SQL server table.
I've seen some solutions where it converts the file into a byte array which can be saved to an SQL table but I'm not sure how to do this in a .NET web api 2 and from an angularjs front end. thank you in advance.
Don't save files to SQL server--that's not what it's for. See this answer: In MVC4, how do I upload a file (an image) to SQL Server that's part of my domain model? And this answer: Storing files in SQL Server
Uploading files in angular is easy. Do it like this:
Controller
$scope.uploadFile = function() {
//get the filename from the <input type='file'>
//angular doesn't allow attaching ngModel to file input
var fileInput = document.getElementById("myInputId");
//check if there's a file
if(fileInput.files.length === 0) return;
//you cannot send a file as JSON because json is in the string format
//for fileuploads, you must send as a FormData() object
//C# accepts HttpPostedFileBase as the file argument
var file = fileInput.files[0];
//put the file in a new formdata object
var payload = new FormData();
payload.append("file", file);
//upload file to C# controller
$http.post("path/to/C#/controller", payload, {
//you **need** to specify these options, without them upload does not work
transformRequest: angular.identity,
headers: { "Content-Type": undefined }
}).then(function(data) {
//success
}, function(error) {
//error
});
}
C#/ASP.NET
[WebMethod]
public string UploadFile(HttpPostedFileBase file) {
//access the file object here
var inputStream = file.InputStream;
var fileName = Path.GetFileName(file.FileName);
try
{
file.SaveAs("local/path" + fileName);
}
catch (IOException exc)
{
return "Error: " + exc.Message;
}
return "success";
}

Dart and Client Side File Handling (with authorization)

A server side application requires authorization on file download links. This means a normal <a ng-href="{{createLinkToFile()}}"> is no longer sufficient to get enough parameters passed to the server.
When trying to use a programmatic call to the file download, I get the response data back to Dart client application. Using a simple http GET:
var url = "http://example.com/file";
headers.putIfAbsent("Authorization", () => "bearer " + token;
_http.get(url: url, headers : headers);
The future returned by the GET will hold the data, but how do I instruct the browser to download it as a file, instead of just trying to keep it in memory?
Or is there a way to just do it in a normal link?
After downloading the data from the server like shown in Using Dart to Download a PNG File (Binary File) and displaying it not working you can create a download link like shown at http://blog.butlermatt.me/2014/03/dynamically-generating-download-files/
import 'dart:html';
void main() {
List body = [ 'Some test data ...\n'];
// Create a new blob from the data.
Blob blob = new Blob(body, 'text/plain', 'native');
// Create a data:url which points to that data.
String url = Url.createObjectUrlFromBlob(blob);
// Create a link to navigate to that data and download it.
AnchorElement link = new AnchorElement()
..href = url
..download = 'random_file.txt'
..text = 'Download Now!';
// Insert the link into the DOM.
var p = querySelector('#text');
p.append(link);
}
The code of Seth solves indeed part of the problem. To make it a bit more complete, I'm now using the following:
void doPdfFileRequest(String url) {
var request = new HttpRequest();
request.open('GET', url);
request.responseType = "blob";
request.withCredentials = false;
request.setRequestHeader("Accept", _httpAcceptHeader);
request.setRequestHeader("Authorization", "bearer " + token);
request.onReadyStateChange
.listen((r) => onData(request, "filename.pdf"));
request.send();
}
void onData(HttpRequest request, String filename) {
if (request.readyState == HttpRequest.DONE && request.status == 200) {
if (!isIE()) {
var contentType = request.getResponseHeader("content-type");
AnchorElement downloadLink = new AnchorElement(
href: Url.createObjectUrlFromBlob(request.response));
downloadLink.rel = contentType;
downloadLink.download = filename;
var event = new MouseEvent("click", view: window, cancelable: false);
downloadLink.dispatchEvent(event);
} else {
var href = Url.createObjectUrlFromBlob(request.response);
window.open(href, "_self");
}
}
}
A few things to notice. Instead of using the downloadLink.click(), a mouse event is constructed to ensure that it works on Firefox as well as on Safari and Chrome. Firefox seems not to handle the click() otherwise. Binding it to the DOM as is done in the code of Seth isn't necessary.
Internet Explorer doesn't understand the download attribute, so nothing will happen, therefore a window.open is used to at least have it work (though not ideal) on IE, it's redirecting to self to avoid being hit by the pop up blocker.
There are solutions that convert the result download result to Base64 first and put it in a data:mimetype href, using the blob this isn't necessary.
A nice way to set the filename on the file to download would be through the content disposition header, but this header is marked as unsafe, so cannot be used. The filename is now set in the code.
Another note, notice that a HttpRequest is used instead http.get(), The HttpRequest allows you to set the responseType, in this case blob, which can be transformed into a object url.

Resources