I'm using the HTML5 Blob
API to download a file from a JavaScript client in a WebView
based macOS application:
/**
* Save a text as file using HTML <a> temporary element and Blob
* @author Loreto Parisi
*/
var saveAsFile = function(fileName,fileContents) {
if(typeof(Blob)!='undefined') { // using Blob
var textFileAsBlob = new Blob([fileContents], { type: 'text/plain' });
var downloadLink = document.createElement("a");
downloadLink.download = fileName;
if (window.webkitURL != null) {
downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
}
else {
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = document.body.removeChild(event.target);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
} else {
var pp = document.createElement('a');
pp.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(fileContents));
pp.setAttribute('download', fileName);
pp.onclick = document.body.removeChild(event.target);
pp.click();
}
}//saveAsFile
When the Blob
is not supported it uses the standard DOM way.
When I run my application within MacGap2 running this code called let's say like saveAsFile('out.json',jsonString);
it will lead to this error:
2018-04-23 19:35:08.270857+0200 sendMessageWithDictionary: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd}
So I have configured the App Sandbox for Outgoing Connections (Client)
,
I have also tried to intercept the link click through the PolicyDelegate
:
- (void)webView:(WebView *)webView decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request newFrameName:(NSString *)frameName decisionListener:(id < WebPolicyDecisionListener >)listener
{
if (WebNavigationTypeLinkClicked == [[actionInformation objectForKey:WebActionNavigationTypeKey] intValue])
{
NSLog(@"CLICKED %@", [request URL]);
}
[[NSWorkspace sharedWorkspace] openURL:[request URL]];
[listener ignore];
}
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener>)listener
{
if (WebNavigationTypeLinkClicked == [[actionInformation objectForKey:WebActionNavigationTypeKey] intValue])
{
NSLog(@"CLICKED %@", [request URL]);
}
[listener use]; // Say for webview to do it work...
}
At this point I can get the Blob url clicked in the latter delegate, so, in the Objective-C / Cocoa Realm, I have approached the following (that currently works on macOS High Sierra / Xcode 9.3 / macOS 10.13
NSLog(@"CLICKED %@", [request URL]);
NSString *needle = @"blob:";
if( [[[request URL] absoluteString] hasPrefix:needle] ) {
// create a download link from blob url
NSRange blobRange = [[[request URL] absoluteString] rangeOfString:needle];
NSString * blobURL = [[[request URL] absoluteString] substringFromIndex:blobRange.location + needle.length];
NSURLRequest *downloadURLRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:blobURL]];
NSLog(@"BLOB URL:%@", [[downloadURLRequest URL] absoluteString]);
NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithRequest:downloadURLRequest
completionHandler:^(NSURL *location, __unused NSURLResponse *response, NSError *error) {
if (location) {
// get download folders
NSArray *docDirs = NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory,
NSUserDomainMask, YES);
NSString *destinationFilename = [docDirs objectAtIndex:0];
if (destinationFilename) {
destinationFilename = [destinationFilename stringByAppendingPathComponent:@"out.json"];
NSLog(@"LOCATION %@ DOWNLOAD %@", [location absoluteString], destinationFilename);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *anError = nil;
NSString *fromPath = [location path];
if ([fileManager fileExistsAtPath:destinationFilename])
[fileManager removeItemAtPath:destinationFilename error:&anError];
BOOL fileCopied = [fileManager moveItemAtPath:fromPath toPath:destinationFilename error:&anError];
if (fileCopied == NO) {
} else {
NSLog(@"Downloaded!");
}
}
} else {
NSLog(@"Error:%@", [error description]);
}
}];
[downloadTask resume];
return;
}
To enable file download I had to additionally enable the Download folders read/write
policy in the App Sandbox. Basically now I can intercept the blob:
urls that is a typical HTML5 Blob
url (blob:https://myserver/BLOB_ID
), and I try to download, but - of course