macOS WebView Download a HTML5 Blob file

匿名 (未验证) 提交于 2019-12-03 02:29:01

问题:

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 ? it does not work because that url it seems not be a valid url, so I get an error back from the file server - of course

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /57de17ae-92bc-4553-a9d8-ac1b0f1f6c4f</pre> </body> </html> 

So despite the fact that the code above is generally working fine for real files (i.e. having a valid URI), I was wrong about the Blob files, since these files are not actual files, so there must be a different way to handle client side Blob urls in a macOS WebView.

[UPDATE] It seems that there are different tricks to make this working via

  • XMLHttpRequest plus FileReader API
  • requestFileSystem + FileWriter API

So I'm posting here https://gist.github.com/loretoparisi/84df6a7c19f411dbf9d0a0d10505e222

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!