How to encode binary file inside JavaScript in HTML - javascript

I want to use a HTML webpage that uses a .bin file with JavaScript.
I have found out that browsers don't support opening files from JS with anything BUT a protocol.
Console in Google Chrome throws the "origin 'null' has been blocked by CORS policy"
HOWEVER, could I paste the binary file's base64 value (e.g. data URI or JSON) inside the .HTML instead of asking it to open the file?
...
var filename = "recipes_0_" + cut_cost + "_" + stack_cost + (singleLayerIsRaw?"r":"") + ".bin";
load_binary_resource( filename );
}
function onPageLoad() {
document.getElementById("code").disabled = true;
//load_binary_resource("[base64 value]");
instead of
document.getElementById("code").disabled = true;
//load_binary_resource("myfile.bin");

Related

How to customize filename in browsers builtin PDF viewer for base64 pdfs

I'm embedding PDFs from a base64 string like this:
var pdfData = 'data:application/pdf;filename=MY_CUSTOM_FILENAME.pdf;base64,' +
'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' +
'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' +
'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' +
'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' +
'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' +
'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' +
'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' +
'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' +
'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' +
'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' +
'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' +
'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' +
'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G';
function loadPdf() {
var pdf= document.querySelector('.pdf')
var pobj = document.createElement('object')
pobj.className= "pdf_object"
pobj.setAttribute('data', pdfData)
pobj.setAttribute('type', 'application/pdf')
pdf.appendChild(pobj)
}
.pdf_object {
width: 100%;
height: 700px;
}
<div>
<button onclick="loadPdf();">
CLICK TO LOAD BELOW THE PDF
</button>
</div>
<div class="pdf"></div>
This works both on Firefox and Chrome.
My problem is I cannot customize the filename for the pdf download actions. Notice that I'm using the filenameparameter ('data:application/pdf;filename=MY_CUSTOM_FILENAME.pdf;...), but it's ignored.
On Firefox, I get this warning (related code is here):
"getPDFFileNameFromURL: ignoring "data:" URL for performance reasons."
And the pdf's filename is the default document.pdf.
On Chrome, I get no warning, but filename sticks to the default download.pdf.
So, how can I customize the filename? Is that possible?
I tried these ways, unsuccessfully:
Checked PDFObject, which is a library doing basically what I'm doing in this post (discovered it while writing this!), but they seem to not allow any customization on the filename.
I tried to load pdf on an <iframe>, and then manipulating its content. But I get (at least in Firefox) a Cross Origin error
It seems that Mozilla's PDF viewer (PDF.js) also checks the Content-Disposition header for guessing out the pdf's filename. Could be possible to somehow simulate an HTTP request (always locally in the browser, as I work with base64 strings) and use it to load the pdf viewer?
I know the solution can be to directly use PDF.js, but if possible I would love to avoid this dependency (and also the coding, cause it's not so straightforward to implement it). At the end, I just want to customize filename!
Thanks!
EDIT: The code snippet below does not work on Chrome (Failed to load 'data:application/pdf;filename=MY_CUSTOM_FILENAME.pdf;base64,....' as a plugin, because the frame into which the plugin is loading is sandboxed.), but it works if you open a plan .html file.
EDIT 2: I found a solution but it just works on Firefox.
We will convert our base64 string to a Blob as explained here, convert this Blob to Url and add a filename parameter to this url (using '#' as explained here).
So, the loadPdf() function is this:
function loadPdf() {
fetch(pdfData)
.then(res => res.blob())
.then(blob => {
try {
var blobUrl = URL.createObjectURL(blob)
// This line makes Chrome crash, we have to remove it there
// But it works on Firefox
blobUrl+= '#filename=MY_CUSTOM_FILENAME.pdf'
var pdf= document.querySelector('.pdf')
var pobj = document.createElement('object')
pobj.className= "pdf_object"
pobj.setAttribute('data', blobUrl)
pobj.setAttribute('type', 'application/pdf')
pdf.appendChild(pobj)
} catch(e) {
console.error(e)
}
})
}
When downloading from Firefox's Pdf viewer, we'll successfully see MY_CUSTOM_FILENAME.pdf.
But on Chrome we will see the Blob url's hash. And:
it seems impossible to change that hash
even if I use an <iframe> (instead of an <object>) to load my inline pdf, Chrome will make use of an <embed>. So, there's no way to "hack" it.
Any other tip from this perspective?

<a> element does not recognize the file extension automatically when downloading a file

I'm currently developing an application where the user clicks on an element, that element calls a JS function and the function handles a file download.
The files are reports generated dynamically by Devexpress XtraReports module, converted to Base64 and then sent back to the client side. When the client receives the Base64 string, the JS function creates an <a> element, sets the href attribute to data:application/pdf;base64,JVBERi0xLjQNCiWio[...] and simulates a click with the click() event.
Here's the piece of JS code that handles the file download:
let downloadLink;
try {
downloadLink = executionId ? await getLinkPdfBase64(executionId) : false;
} catch (error) {
downloadLink = false;
console.log(error);
}
if (downloadLink) {
const aElement = document.createElement("a");
downloadLink = "data:application/pdf;base64," + downloadLink;
aElement.setAttribute("download", currentReportData.LayoutName);
aElement.setAttribute("href", downloadLink);
aElement.click();
aElement.remove();
} else {
DevExpress.ui.dialog.alert( //Ignore this, it's a Devexpress component
"Your report could not be generated",
"Alert"
);
}
The problem is:
When I generate a report with custom parameter types, Devexpress generates it correctly (the Base64, if converted to string, is visibly correctly formed) but the browser (Google Chrome) downloads the file with the extension ".0".
If the report has normal Devexpress parameters (like Strings, Int32, Guids, etc)) the file is downloaded with the correct ".pdf" extension.
Here's a picture of a correctly downloaded PDF and a ".0" extension file:
Could it be the JS function the cause or the solution to the problem? If not, almost for sure there will be something wrong with the report generator (Devexpress).
NB: If I manually change the ".0" extension to ".pdf" the file opens and it is displayed / formed correctly.
Turns out I ended up solving it just by adding the file extension ".pdf" in the download attribute, so when the browser can't recognize it, you are already specifying which one it is:
aElement.setAttribute("download", currentReportData.LayoutName + ".pdf");

Redirect from php file to a local HTML file [duplicate]

Test browser:
Version of Chrome: 52.0.2743.116
It is a simple javascript that is to open an image file from local like 'C:\002.jpg'
function run(){
var URL = "file:///C:\002.jpg";
window.open(URL, null);
}
run();
Here is my sample code.
https://fiddle.jshell.net/q326vLya/3/
Please give me any suitable suggestions.
We use Chrome a lot in the classroom and it is a must to working with local files.
What we have been using is "Web Server for Chrome". You start it up, choose the folder wishing to work with and go to URL (like 127.0.0.1:port you chose)
It is a simple server and cannot use PHP but for simple work, might be your solution:
https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb
1)
Open your terminal and type
npm install -g http-server
2)
Go to the root folder that you want to serve you files and type:
http-server ./
3)
Read the output of the terminal, something kinda http://localhost:8080 will appear.
Everything on there will be allowed to be got.
Example:
background: url('http://localhost:8080/waw.png');
Okay folks, I completely understand the security reasons behind this error message, but sometimes, we do need a workaround... and here's mine. It uses ASP.Net (rather than JavaScript, which this question was based on) but it'll hopefully be useful to someone.
Our in-house app has a webpage where users can create a list of shortcuts to useful files spread throughout our network. When they click on one of these shortcuts, we want to open these files... but of course, Chrome's error prevents this.
This webpage uses AngularJS 1.x to list the various shortcuts.
Originally, my webpage was attempting to directly create an <a href..> element pointing at the files, but this produced the "Not allowed to load local resource" error when a user clicked on one of these links.
<div ng-repeat='sc in listOfShortcuts' id="{{sc.ShtCut_ID}}" class="cssOneShortcutRecord" >
<div class="cssShortcutIcon">
<img ng-src="{{ GetIconName(sc.ShtCut_PathFilename); }}">
</div>
<div class="cssShortcutName">
<a ng-href="{{ sc.ShtCut_PathFilename }}" ng-attr-title="{{sc.ShtCut_Tooltip}}" target="_blank" >{{ sc.ShtCut_Name }}</a>
</div>
</div>
The solution was to replace those <a href..> elements with this code, to call a function in my Angular controller...
<div ng-click="OpenAnExternalFile(sc.ShtCut_PathFilename);" >
{{ sc.ShtCut_Name }}
</div>
The function itself is very simple...
$scope.OpenAnExternalFile = function (filename) {
//
// Open an external file (i.e. a file which ISN'T in our IIS folder)
// To do this, we get an ASP.Net Handler to manually load the file,
// then return it's contents in a Response.
//
var URL = '/Handlers/DownloadExternalFile.ashx?filename=' + encodeURIComponent(filename);
window.open(URL);
}
And in my ASP.Net project, I added a Handler file called DownloadExternalFile.aspx which contained this code:
namespace MikesProject.Handlers
{
/// <summary>
/// Summary description for DownloadExternalFile
/// </summary>
public class DownloadExternalFile : IHttpHandler
{
// We can't directly open a network file using Javascript, eg
// window.open("\\SomeNetworkPath\ExcelFile\MikesExcelFile.xls");
//
// Instead, we need to get Javascript to call this groovy helper class which loads such a file, then sends it to the stream.
// window.open("/Handlers/DownloadExternalFile.ashx?filename=//SomeNetworkPath/ExcelFile/MikesExcelFile.xls");
//
public void ProcessRequest(HttpContext context)
{
string pathAndFilename = context.Request["filename"]; // eg "\\SomeNetworkPath\ExcelFile\MikesExcelFile.xls"
string filename = System.IO.Path.GetFileName(pathAndFilename); // eg "MikesExcelFile.xls"
context.Response.ClearContent();
WebClient webClient = new WebClient();
using (Stream stream = webClient.OpenRead(pathAndFilename))
{
// Process image...
byte[] data1 = new byte[stream.Length];
stream.Read(data1, 0, data1.Length);
context.Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", filename));
context.Response.BinaryWrite(data1);
context.Response.Flush();
context.Response.SuppressContent = true;
context.ApplicationInstance.CompleteRequest();
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
And that's it.
Now, when a user clicks on one of my Shortcut links, it calls the OpenAnExternalFile function, which opens this .ashx file, passing it the path+filename of the file we want to open.
This Handler code loads the file, then passes it's contents back in the HTTP response.
And, job done, the webpage opens the external file.
Phew ! Again - there is a reason why Chrome throws this "Not allowed to load local resources" exception, so tread carefully with this... but I'm posting this code just to demonstrate that this is a fairly simple way around this limitation.
Just one last comment: the original question wanted to open the file "C:\002.jpg". You can't do this. Your website will sit on one server (with it's own C: drive) and has no direct access to your user's own C: drive. So the best you can do is use code like mine to access files somewhere on a network drive.
Chrome specifically blocks local file access this way for security reasons.
Here's an article to workaround the flag in Chrome (and open your system up to vulnerabilities):
http://www.chrome-allow-file-access-from-file.com/
There is a workaround using Web Server for Chrome. Here are the steps:
Add the Extension to chrome.
Choose the folder (C:\images) and launch the server
on your desired port.
Now easily access your local file:
function run(){
// 8887 is the port number you have launched your serve
var URL = "http://127.0.0.1:8887/002.jpg";
window.open(URL, null);
}
run();
PS: You might need to select the CORS Header option from advanced setting incase you face any cross origin access error.
This issue come when I am using PHP as server side language and the work around was to generate base64 enconding of my image before sending the result to client
$path = 'E:/pat/rwanda.png';
$type = pathinfo($path, PATHINFO_EXTENSION);
$data = file_get_contents($path);
$base64 = 'data:image/' . $type . ';base64,' . base64_encode($data);
I think may give someone idea to create his own work around
Thanks
Google Chrome does not allow to load local resources because of the security. Chrome need http url. Internet Explorer and Edge allows to load local resources, but Safari, Chrome, and Firefox doesn't allows to load local resources.
Go to file location and start the Python Server from there.
python -m SimpleHttpServer
then put that url into function:
function run(){
var URL = "http://172.271.1.20:8000/" /* http://0.0.0.0:8000/ or http://127.0.0.1:8000/; */
window.open(URL, null);
}
If you have php installed - you can use built-in server. Just open target dir with files and run
php -S localhost:8001
If you could do this, it will represent a big security problem, as you can access your filesystem, and potentially act on the data available there... Luckily it's not possible to do what you're trying to do.
If you need local resources to be accessed, you can try to start a web server on your machine, and in this case your method will work. Other workarounds are possible, such as acting on Chrome settings, but I always prefer the clean way, installing a local web server, maybe on a different port (no, it's not so difficult!).
See also:
Open local files(file://) using Chrome
Opening local files from chrome
You just need to replace all image network paths to byte strings in stored Encoded HTML string.
For this you required HtmlAgilityPack to convert Html string to Html document.
https://www.nuget.org/packages/HtmlAgilityPack
Find Below code to convert each image src network path(or local path) to byte sting.
It will definitely display all images with network path(or local path) in IE,chrome and firefox.
string encodedHtmlString = Emailmodel.DtEmailFields.Rows[0]["Body"].ToString();
// Decode the encoded string.
StringWriter myWriter = new StringWriter();
HttpUtility.HtmlDecode(encodedHtmlString, myWriter);
string DecodedHtmlString = myWriter.ToString();
//find and replace each img src with byte string
HtmlDocument document = new HtmlDocument();
document.LoadHtml(DecodedHtmlString);
document.DocumentNode.Descendants("img")
.Where(e =>
{
string src = e.GetAttributeValue("src", null) ?? "";
return !string.IsNullOrEmpty(src);//&& src.StartsWith("data:image");
})
.ToList()
.ForEach(x =>
{
string currentSrcValue = x.GetAttributeValue("src", null);
string filePath = Path.GetDirectoryName(currentSrcValue) + "\\";
string filename = Path.GetFileName(currentSrcValue);
string contenttype = "image/" + Path.GetExtension(filename).Replace(".", "");
FileStream fs = new FileStream(filePath + filename, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
Byte[] bytes = br.ReadBytes((Int32)fs.Length);
br.Close();
fs.Close();
x.SetAttributeValue("src", "data:" + contenttype + ";base64," + Convert.ToBase64String(bytes));
});
string result = document.DocumentNode.OuterHtml;
//Encode HTML string
string myEncodedString = HttpUtility.HtmlEncode(result);
Emailmodel.DtEmailFields.Rows[0]["Body"] = myEncodedString;
Chrome and other Browser restrict the access of a server to local files due to security reasons. However you can open the browser in allowed access mode. Just open the terminal and go to the folder where chrome.exe is stored and write the following command.
chrome.exe --allow-file-access-from-files
Read this for more details
This way, However, didn't work for me so I made a different route for every file in a particular directory. Therefore, going to that path meant opening that file.
function getroutes(list){
list.forEach(function(element) {
app.get("/"+ element, function(req, res) {
res.sendFile(__dirname + "/public/extracted/" + element);
});
});
}
I called this function passing the list of filename in the directory __dirname/public/extracted and it created a different route for each filename which I was able to render on server side.
This is for google-chrome-extension
const url = "file:///C:\002.jpg"
chrome.tabs.create({url, active:true})
manifest.json
{
"name": "",
"manifest_version": 3,
"permissions": [
"activeTab",
"tabs"
],
// ...
}
This solution worked for me in PHP. It opens the PDF in the browser.
// $path is the path to the pdf file
public function showPDF($path) {
if($path) {
header("Content-type: application/pdf");
header("Content-Disposition: inline; filename=filename.pdf");
#readfile($path);
}
}
I've encounterd this problem, and here is my solution for Angular, I wrapped my Angular's asset folder in encodeURIComponent() function. It worked. But still, I'd like to know more about the risk of this solution if there's any:
```const URL = ${encodeURIComponent(/assets/office/file_2.pdf)}
window.open(URL)
I used Angular 9, so this is my url when I clicked open local file:
```http://localhost:4200/%2Fassets%2Foffice%2Ffile_2.pdf```
In the case of audio files, when you give <audio src="C://somePath"/>, this throws an error saying cannot load local resource.
This makes sense because any webpage can't simply give a local path and access your private files.
In case you are trying to play audio with dynamic paths, by changing src property through JS, then here is a sample implementation using Flask server and HTML.
server.py
#app.route("/")
def home():
return render_template('audioMap.html')
#app.route('/<audio_file_name>')
def view_method(audio_file_name):
path_to_audio_file = "C:/Audios/yourFolderPath" + audio_file_name
return send_file(
path_to_audio_file,
mimetype="audio/mp3",
as_attachment=True,
attachment_filename="test.mp3")
audioMap.html
{% raw %}
<!DOCTYPE html>
<html>
<body>
AUDIO: <audio src="Std.mp3" controls >
</body>
</html>
{% endraw %}
Explanation:
When you give the audio file name under src property, this creates a get request in the flask as shown
127.0.0.1 - - [04/May/2021 21:33:12] "GET /Std.mp3 HTTP/1.1" 200 -
As you can see that, the flask has sent a Get request for the Std.mp3 file. So to serve this get request, we wrote an endpoint that takes the audio file name, reads it from the local directory, and returns it back. Hence the audio shows up on UI.
Note: This works only if you are rendering your HTML file using the
render_template method via flask or to say, using flask as your web server.
Google Chrome does not allow to load local resources because of the security .
There is a simple solution for this problem .
1.install live-server plugin in vscode
2.open the html file by live-server

Browser requests file, server downloads pdf, browser displays pdf in iframe

this is what I want to do:
1) a browser initiates an ajax request to the server, asking for a pdf.
2) the server downloads the pdf, and returns the pdf for display.
3) the browser displays the downloaded pdf in a pre-existing iframe.
Below is my code. It appears to stop at the iframe part, but I suspect that its not sending the pdf properly.
Browser index.html file:
var uri = '/viewer/loaddrawing/';
$.getJSON(uri, {key:value}, function(data, jqXHR){
document.getElementById("iframetitle").src = uri;
});
Django server views.py file:
import requests
def loaddrawing(request):
value = request.GET.get('key')
#the key is used to generate a unique url, but for test purposes lets use the url shown below
url = "http://cbmeturkey.com/media/109/test.pdf"
response = urllib2.urlopen(url)
some_data = response.read()
return HttpResponse(some_data, mimetype='application/pdf')
EDIT:
I have one issue remaining: I don't want to use the below code in my index.html file anyway, because I want to actually download the pdf and use it again. The reason for this is that my index.html page uses javascript to display and hide the iframe, and with the below code the pdf is redownloaded each time the iframe is shown.
var uri = '/viewer/loaddrawing/';
document.getElementById('iframetitle').src = uri + '?key=' + value;
SOLVED:
The above issue was solved after directions from Augusto, and the following index.html code, which loads the pdf only once, although notice that now I am now modifying a div (named "divtitle") and not an iframe:
var uri = '/viewer/loaddrawing/' + '?key=' + value;
var htm = '\<iframe src="' + uri +'" onload="downloadComplete()">\</iframe>';
document.getElementById('divtitle').innerHTML = htm;
Probably what you meant was simply:
var uri = '/viewer/loaddrawing/';
document.getElementById('iframetitle').src = uri + '?key=' + value;
There is no JSON involved, so the iframe will get a PDF directly. However, notice that the client must have a PDF-viewer plugin, otherwise the browser will ask for a download prompt.

Javascript to get .pdf file from partial match

Complete and utter javascript newbie here with a problem fetching .pdf files from a web server based on a partial match. I have made a program that outputs data to a webserver, and one of the components is a folder of .pdf files. I want to be able to click on a link that will pull up the corresponding .pdf file based on a value in the data table that's generated (I'm using slickgrid for this). Each of the .pdf files contains the value that's in the data table and serves as good query to the .pdf folder, and I've been successful at getting the .pdfs I want with the following code:
var value = grid.getData().getItem(row)['data'];
var locpath = window.location.pathname.substring(0,window.location.pathname.lastIndexOf('/'));
var plotsFolder = window.location.protocol + "//" + window.location.host + locpath + "/CovPlots/";
var href = plotsFolder + value + ".pdf";
return "<a href='" + href + "'>" + value + "</a>";
The catch here is that sometimes the .pdf file that's generated is a concatenation of two or more (I've seen up to 4 so far) of the 'data' strings, separated by '_' as a delimiter for reasons not worth getting into. So, if the .pdf file is 'somestring.pdf', I can get it without problem. However, if the .pdf file is 'somestring_anotherstring.pdf', I can't figure out how to get that .pdf file if I have either 'somestring' or 'anotherstring' as the value of 'data'.
I've tried a ton of different things to get some kind of lookup that I can use to pull down the correct file based on a partial match. The latest attempt is with the FilenameFilter object in javascript, but without any knowledge of javascript, I'm having a hard time to get it working. I tried to create a new function that I could call as a lookup for the .pdf URL:
function lookup() {
File directory = new File(plotsFolder);
String[] myFiles = directory.list(new FilenameFilter() {
public boolean accept(File directory, String fileName) {
return fileName.match(value);
}
});
}
That only seems to thrown an error. Can anyone point me in the right direction to be able to download the correct .pdf file based on a partial match? I also tried to see if there was a jquery way to do it, but couldn't seem to find something that works. Thanks in advance!
Without support from the server, JavaScript cannot find a file from a partial filename. What you can do, however, is have a little script on the server that does the partial-filename-matching for JavaScript, and then JavaScript can ask the server to do the match, and then when it gets the match back, it can use that filename.
If you don't mind loading a whole index of all the PDFs at once, you could use this little Python script to generate an index in a nice, JavaScript-friendly JSON format:
#!/usr/bin/env python
# Create an index of a bunch of PDF files.
# Usage: python make_index.py directory_with_pdf_files
import os
import sys
import json
def index(directory):
index = {}
for filename in os.listdir(directory):
base, ext = os.path.splitext(filename)
if ext.lower() != '.pdf':
continue
for keyword in base.split('_'):
index[keyword] = filename
with open(os.path.join(directory, 'index.json'), 'w') as f:
f.write(json.dumps(index))
if __name__ == '__main__':
index(sys.argv[1])
Then you can just load index.json with jQuery or what-have-you. When you need to find a particular PDF's filename, you can do something like this (assuming the object loaded from index.json is in the indexOfPDFs variable):
var href = plotsFolder + indexOfPDFs[value];

Categories