base64 embeded PDF files won't render in Chrome - html

Some PDF files won't render in Chrome browser but will render fine in Firefox. All files render fine in all browsers if emeded directly.
<object id="content-view" :data="content_view.base64" type="application/pdf"></object>
The confusing part is that the problem is only for some files and not all. Files are stored in a folder that is not public and that's why they are served as base64 for the user to view.
I tested the problematic files by using online base64 decoders and I get the same result. Rendered in FF, not rendered in Chrome.
I cannot share any of the PDF files. They are all from the same source, scanned from the same device, PDF version 1.4, 4 pages.
I have tried:
using iframe, embed and object (same result)
unblocking Insecure content in Chrome site settings
opening and re-saving in Adobe Acrobat
using online PDF analyzers to see if any problems present (none found)

I had the exact same issue. I noticed that some of the PDFs that are more than 1MB isn't loading.
I found a solution here: Open base64 encoded pdf file using javascript. Issue with file size larger than 2 MB
Need to change the Base64 string to a BLOB. Then create a URL to be used for iframe src.
Here is the code:
base64PDFToBlobUrl( base64 ) {
const binStr = atob( base64 );
const len = binStr.length;
const arr = new Uint8Array(len);
for (let i = 0; i < len; i++) {
arr[ i ] = binStr.charCodeAt( i );
}
const blob = new Blob( [ arr ], { type: 'application/pdf' } );
const url = URL.createObjectURL( blob );
return url;
}
This will return a url that you can put inside your iframe, embed or object src. This way, you can still load the PDF in a page without opening it in another tab.

Rather than use the browsers native PDF renderer, you could use the JS one written by Mozilla.
ViewerJS provides a nice interface to this and if you want to embed it fullsize in a page, then you can place it in an iframe and control that with iFrame-resizer.

Instead of opening the PDF file in HTML object element, open it in a new window using Blob URL.
Please refer
Creating a BLOB from a Base64 string in JavaScript
to convert Base64 to Blob

Related

HTML <object> - download file if it cannot be loaded

I have a web application that serves files for viewing. If it's a PDF, I simply attach it to an <object> element. However, the app supports serving word, excel, and powerpoint files. I have tried looking for ways to preview them online, but apparently we do not have the proper technology for that (at least not natively in a browser). So instead, I want the user to download the file to view locally.
The front-end is built with React and the back-end with Spring Boot. Currently, all static resources that are documents (PDF's, docs, spreadsheets, etc.) are served under the "/document-files/**" ant-matcher. Additionally, these resources can only be viewed privately, meaning that you have to be logged in to the application to view them. Here's how part of my SecurityConfig file looks like:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String documentRootPath = "file:" + this.documentRootPath;
registry.addResourceHandler("/resources/**").addResourceLocations("resources/");
registry.addResourceHandler("/document-files/**").addResourceLocations(documentRootPath);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // ant matchers below are api routes and the baseUri for serving documents
.antMatchers("/clients/**", "/projects/**", "/documents/**", "/document-files/**").authenticated()
.anyRequest().permitAll() //... additional method chaining omitted for brevity
}
The problem is apparently just on the front end. I don't think it has anything to do with the configuration, but I posted it for reference. With this configuration I can download and preview PDF files just fine, using <object> but for all other files, the file does not load, so in <object> I add a link to open the file like so:
render() {
// some code omitted for brevity
return (
<Module>
{!this.state.currDoc ? (
<ul className={this.state.displayType}>{list}</ul>
) : (
<object
data={"/document-files" + this.state.filePath}
type={this.mimeTypes[document.fileType]}
title={"Current Document: " + document.description + "." + document.fileType.toLowerCase()}>
{document.fileType === "PDF" ? "File could not be loaded!" : <div id="download-prompt">This file cannot be previewed online, click below to download and open locally.<a href={"http://localhost:3000/document-files" + this.state.filePath} download>Open</a></div>}
</object>
)}
</Module>
);
}
Upon clicking, the "save as" dialog box appears with the file name populated and the correct mime type but once I hit save, Chrome displays "Failed - No File". I have tried writing the href with and without the hostname. I've also tried removing the download attribute, but it redirects the page back to itself. I've even tried onLoad attribute on <object> but apparently that only works for images. I checked the network tab on dev tools and there is no record of the file being downloaded, unlike PDFs where the request is noted down.
How can I make non-PDF files download correctly using this setup?
Thanks to javilobo8's GitHubGist, I found a way to download files using Axios, which library I was already using in my app to begin with. For quick reference, here is his code:
axios({
url: 'http://localhost:5000/static/example.xlsx',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf');
document.body.appendChild(link);
link.click();
});
Also, there's multiple ways to work with Blobs, depending on if you're in IE, a browser that supports HTML5's Blob object, or another browser. This question helps split the code in 3 ways to form the dataUri for downloading raw data.
After setting up my <a> tag, I just trigger the click event and the non-PDF file downloads!

Images downloaded from s3, how to save them

After downloading an image from s3, I get a bunch of bits inside the 'Body'. Previously, I was encoding such bits into base64 and displaying them directly in a <img> tag.
But now, I want to save them into a "real" file (.jpg/png) in the filesystem (android/iOS) then give the path to a html (background) tag.
(will it work?)
Do I still need to convert the bunch of bits I got from S3 into base64 then save it or is there a more straightforward way ?
You can use the plugin cordova-plugin-file for saving file locally to your filesystem.
Like this
var url = url;
var targetPath = cordova.file.externalDataDirectory + NAME;
//alert(targetPath);
var trustHosts = true;
var options = {};
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
I think you can give the local file address to img src..

toDataURL Image download has no extension

I am downloading an image when a div is clicked by using...
document.location.href = save_canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
This is working but the image that downloads has no extension and is just called 'download'
I have tried setting the name like this...
document.location.download = "myfile.jpg";
document.location.href = save_canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
But it is having no effect, where am i going wrong?
The download attribute is not part of the Location object as document.location holds, only for the HTML anchor (A) tags (except in IE).
Depending on browser and version you could instead try to convert canvas into a Blob object, then to File in order to set a filename, and serve that as URL via URL.createObjectURL(). Also here, toBlob() is not supported in IE (but you can polyfill toBlob(), and use msSaveBlob instead).
(and you would also want to replace mime-type's "image" with "application" for mime-type (e.g. "application/octet-stream"). )
c.toBlob(function(blob) {
var file = new File([blob], "test.png", {type: "application/octet-stream"});
document.location.href = URL.createObjectURL(file);
})
A save request with PNG and filename should appear when running this code...
<canvas id=c></canvas>
Optionally, try the FileSaver.js library which deals with many special cases.

What does "blob" mean in the `href` property in "<link>"? [duplicate]

My page generates a URL like this: "blob:http%3A//localhost%3A8383/568233a1-8b13-48b3-84d5-cca045ae384f" How can I convert it to a normal address?
I'm using it as an <img>'s src attribute.
A URL that was created from a JavaScript Blob can not be converted to a "normal" URL.
A blob: URL does not refer to data the exists on the server, it refers to data that your browser currently has in memory, for the current page. It will not be available on other pages, it will not be available in other browsers, and it will not be available from other computers.
Therefore it does not make sense, in general, to convert a Blob URL to a "normal" URL. If you wanted an ordinary URL, you would have to send the data from the browser to a server and have the server make it available like an ordinary file.
It is possible convert a blob: URL into a data: URL, at least in Chrome. You can use an AJAX request to "fetch" the data from the blob: URL (even though it's really just pulling it out of your browser's memory, not making an HTTP request).
Here's an example:
var blob = new Blob(["Hello, world!"], { type: 'text/plain' });
var blobUrl = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest;
xhr.responseType = 'blob';
xhr.onload = function() {
var recoveredBlob = xhr.response;
var reader = new FileReader;
reader.onload = function() {
var blobAsDataUrl = reader.result;
window.location = blobAsDataUrl;
};
reader.readAsDataURL(recoveredBlob);
};
xhr.open('GET', blobUrl);
xhr.send();
data: URLs are probably not what you mean by "normal" and can be problematically large. However they do work like normal URLs in that they can be shared; they're not specific to the current browser or session.
another way to create a data url from blob url may be using canvas.
var canvas = document.createElement("canvas")
var context = canvas.getContext("2d")
context.drawImage(img, 0, 0) // i assume that img.src is your blob url
var dataurl = canvas.toDataURL("your prefer type", your prefer quality)
as what i saw in mdn, canvas.toDataURL is supported well by browsers. (except ie<9, always ie<9)
For those who came here looking for a way to download a blob url video / audio, this answer worked for me. In short, you would need to find an *.m3u8 file on the desired web page through Chrome -> Network tab and paste it into a VLC player.
Another guide shows you how to save a stream with the VLC Player.
UPDATE:
An alternative way of downloading the videos from a blob url is by using the mass downloader and joining the files together.
Download Videos Part
Open network tab in chrome dev tools
Reload the webpage
Filter .m3u8 files
Look through all filtered files and find the playlist of the '.ts' files. It should look something like this:
You need to extract those links somehow. Either download and edit the file manually OR use any other method you like. As you can see, those links are very similar, the only thing that differs is the serial number of the video: 's-0-v1-a1.ts', 's-1-v1-a1.ts' etc.
https://some-website.net/del/8cf.m3u8/s-0-v1-a1.ts
https://some-website.net/del/8cf.m3u8/s-1-v1-a1.ts
https://some-website.net/del/8cf.m3u8/s-2-v1-a1.ts
and so on up to the last link in the .m3u8 playlist file. These .ts files are actually your video. You need to download all of them.
For bulk downloading I prefer using the Simple Mass Downloader extension for Chrome (https://chrome.google.com/webstore/detail/simple-mass-downloader/abdkkegmcbiomijcbdaodaflgehfffed)
If you opt in for the Simple Mass Downloader, you need to:
a. Select a Pattern URL
b. Enter your link in the address field with only one modification: that part of the link that is changing for each next video needs to be replaced with the pattern in square brackets [0:400] where 0 is the first file name and 400 is the last one. So your link should look something like this https://some-website.net/del/8cf.m3u8/s-[0:400]-v1-a1.ts.
Afterwards hit the Import button to add these links into the Download List of Mass Downloader.
c. The next action may ask you for the destination folder for EACH video you download. So it is highly recommended to specify the default download folder in Chrome Settings and disable the Select Destination option in Chrome Settings as well. This will save you a lot of time! Additionally you may want you specify the folder where these files will go to:
c1. Click on Select All checkbox to select all files from the Download List.
c2. Click on the Download button in the bottom right corner of the SMD extension window. It will take you to next tab to start downloading
c3. Hit Start selected. This will download all vids automatically into the download folder.
That is it! Simply wait till all files are downloaded and you can watch them via the VLC Player or any other player that supports the .ts format. However, if you want to have one video instead of those you have downloaded, you need to join all these mini-videos together
Joining Videos Part
Since I am working on Mac, I am not aware of how you would do this on Windows. If you are the Windows user and you want to merge the videos, feel free to google for the windows solution. The next steps are applicable for Mac only.
Open Terminal in the folder you want the new video to be saved in
Type: cat and hit space
Open the folder where you downloaded your .ts video. Select all .ts videos that you want to join (use your mouse or cmd+A)
Drag and drop them into the terminal
Hit space
Hit >
Hit Space
Type the name of the new video, e.g. my_new_video.ts. Please note that the format has to be the same as in the original videos, otherwise it will take long time to convert and even may fail!
Hit Enter. Wait for the terminal to finish the joining process and enjoy watching your video!
Found this answer here and wanted to reference it as it appear much cleaner than the accepted answer:
function blobToDataURL(blob, callback) {
var fileReader = new FileReader();
fileReader.onload = function(e) {callback(e.target.result);}
fileReader.readAsDataURL(blob);
}
I'm very late to the party.
If you want to download the content you can simply use fetch now
fetch(blobURL)
.then(res => res.blob())
.then(blob => /*do what you want with the blob here*/)
Here the solution:
let blob = new Blob(chunks, { 'type' : 'video/mp4;' });
let videoURL = window.URL.createObjectURL(blob);
const blobF = await fetch(videoURL).then(res => res.blob())
As the previous answer have said, there is no way to decode it back to url, even when you try to see it from the chrome devtools panel, the url may be still encoded as blob.
However, it's possible to get the data, another way to obtain the data is to put it into an anchor and directly download it.
<a href="blob:http://example.com/xxxx-xxxx-xxxx-xxxx" download>download</a>
Insert this to the page containing blob url and click the button, you get the content.
Another way is to intercept the ajax call via a proxy server, then you could view the true image url.

How to embed a Base64 encoded PDF data URI into a HTML 5 `<object>` data attribute?

So in my application, users have the option to upload a file to an <input type = "file" id = "my-file"> (HTML 5 File input). I can subsequently grab this file using the following Javascript:
var files = $("#my-file").files;
var file = files[0];
Can I somehow use this var file as the data in an <object> tag? Here is an example of an object tag where the PDF is grabbed by hitting a URL and contacting the server.
<object data="data/test.pdf" /*<-- I want the var file here */ type="application/pdf" width="300" height="200">
</object>
How can I accomplish this? Please note, I must support Internet Explorer 11.
UPDATE:
I've tried something that ultimately ended in failure. I converted the PDF to a data-uri using FileReader and then used that in the data attribute of the <object> tag, which renders perfectly in Chrome but not at all in Internet explorer.
var files = $(e.currentTarget.files);
file = files[0];
var reader = new FileReader();
reader.onloadend = function() {
var data = reader.result;
console.log(data);
$("#content").prepend('<object id="objPdf" data="'+data+'" type="application/pdf"></object>');
};
reader.readAsDataURL(file);
Where the reader data comes out looking like:
data:application/pdf;base64,JVBERi0xLjQKJe...
Here is the reason PDF's don't work with this approach in Internet Explorer (images only)...so sad :(
UPDATE 2:
Tried pdf.js with some success...it displays the PDF on the page, although it is incredibly slow (5 seconds for a single page) in Internet Explorer 11. It also only displays one page at a time, whereas the <object> tag displayed all pages instantly in a nice scrollable container. While this is an extreme fallback option, I'd like to not go down this route.
Anyone have any other idea as to how I can preview the PDF's that the user uploads directly in the webpage?
Yes I got it working with a file...
HTML:
<object id="pdf" data="" type="application/pdf"></object>
Javascript (Jquery)
var fr = new FileReader();
fr.onload = function(evtLoaded) {
$('#pdf').attr('data', evtLoaded.target.result );
};
fr.readAsDataURL(inFile);
Compared to your approach I use the 'I am done reading the file'-callback differently by using the event.
If I got it right: 'loadend' will always be called no matter if reading succeeded or failed.
Appendix on 04AUG2021:
#Adil talks about a plural of PDFs. Well, this solution talks about 1, I never tried several PDFs. Since the solution is made by an html 'id' we know that the 'id' comes with a singleton pattern per page. Nevertheless, I am convinced it is doable with severla PDFs per page somehow with whatever use case one creates.
#netotz I did not investigate here. It just comes to my mind that it could come to issues regarding the hardware. You do not mention the browser where it happens nor any operating system internals... I just guess (though I can be wrong) that 1.8 MB is rather a small amount of data...
In addition to the above answer and the comments from Dirk's answer.(Since I don't have 50 reputation to comment directly)
I want to point out that there is a way to prevent the issue that
<object> has an issue rendering base64 data directly when the file size is bigger than 1mb.
Steps:
Convert base64 to blob
Create URL from blob
use the URL as the data's src
A good post can be found here: an answer for another pdf lib. But this also suitable for our case.