in my webpage you can read book in pdf format. The problem is that some books have around 1000 pages and the PDF is really big so even if the user reads just 10 pages the server download the full pdf, so this is awful for my hosting account because I have a transfer limit.
What could I do to display the pdf without load the full PDF.
I use pdf.js
Greetings.
ORIGINAL POST:
PDF files are designed in a way that forces the client side to download the whole file just to get the first page.
The last line of the PDF file tells the PDF reader where the root dictionary for the PDF file is located (the root dictionary tells the reader about the page catalog - order of pages - and other data used by the reader).
So, as you can see, the limitations of the PDF design require that you use a server side solution that will create a new PDF with only the page(s) you want to display.
The best solution (in my opinion) is to create a "reader" page (as opposed to a download page) that requests a specific page from the server and allows the user to advance page by page (using AJAX).
The server will need to create a new PDF (file or stream) that contains only the requested page and return it to the reader.
if you are running your server with Ruby (ruby on rails), you can use the combine_pdf gem to load the pdf and send just one page...
You can define a controller method that will look something like this:
def get_page
# read the book
book = CombinePDF.parse IO.read("book.pdf")
# create empty PDF
pdf_with_one_page = CombinePDF.new
# add the page you want
# notice that the pages array is indexed from 0,
# so an adjustment to user input is needed...
pdf_with_one_page << book.pages[ params[:page_number] - 1 ]
# no need to create a file, just stream the data to the client.
send_data pdf_with_one_page.to_pdf, type: 'application/pdf', disposition: 'inline'
end
if you are running PHP or node.js, you will need to find a different server-side solution.
Good luck!
EDIT:
I was looking over the PDF.js project (which looks very nice) and notice the limited support statement for Safari:
"Safari (desktop and mobile) lacks a number of features or has defects, e.g. in typed arrays or HTTP range requests"...
I understand from this statement that on some browsers you can manage a client-side solution based on the HTTP Byte Serving protocol.
This will NOT work with all browsers, but it will keep you from having to use a server-side solution.
I couldn't find the documentation for the PDF.js feature (maybe it defaults to ranges and you just need to set the range...?), but I would go with a server-side solution that I know to work on all browsers.
EDIT 2:
Ignore Edit 1, as iPDFdev pointed out (thank you iPDFdev), this requires a special layout of the PDF file and will not resolve the issue of the browser downloading the whole file.
You can take following approach governed by functionality
Add configuration (i.e. kind of flag) whether you want to display entire PDF or not.
While rendering your response read above mentioned configuration if flag is set generate minimal PDF with 20 pages with hyperlink to download entire PDF else minimal PDF with 20 pages only
When you prepare initial response of your web page add PDF which contains say 20 pages (minimal PDF) only and process the response
Related
In my webapp the user has the option to download a file containing some data, which they do by clicking on a button. For small amounts of data the file starts downloading pretty much immediately and that shows in the browser's download area. Which is good.
For large amounts of data it can take the server a substantial amount of time to calculate the data, even before we start downloading. This is not good. I want to indicate that the calculation is in progress. However I don't want to put a "busy" indicator on my UI, because the action does not block the UI - the user should be able to do other things while the file is being prepared.
A good solution from my point of view would be to start the download process before I have finished the calculation. We always know (or can quickly calculate) the first few hundred bytes of the file. Is there a mechanism where I can have the server respond to a download request with those few bytes, thus starting the download and making the file show up in the download area, and provide the rest of the file when I have finished calculating it? I'm aware that it will look like the download is stalled, and that's not a problem.
I can make a pretty good estimate of the file size very quickly. I would prefer not to have to use a third-party package to achieve this, unless it's a very simple one. We are using Angular but happy to code raw JS if needed.
To indicate that the link points to a download on the client, the easiest way is the download attribute on the link. The presence of the attribute tells the browser not to unload the current tab or create a new one; the value of the attribute is the suggested filename.
For the back-end part, after setting the correct response headers, just write the data to the output stream as it becomes available.
You asked for a general solution
1) First, at your HTML/JS you can prevent the UI from being blocked by setting you download target to any other WebPage, the preferred way for doing this is to set the target to an IFRAME:
<!-- your link must target the iframe "downloader-iframe" -->
<a src="../your-file-generator-api/some-args?a=more-args" target="downloader-iframe">Download</a>
<!-- you don't need the file to be shown -->
<iframe id="downloader-iframe" style="display: none"></iframe>
2) Second, at your back-end you'll have to use both Content-Disposition and Content-Length(optional) headers, be careful using the "length" one, if you miss calculate the fileSize it will not be downloaded. If you don't use Content-Length you'll not see the "downloading progress".
3) Third, at you'r back-end you have to make sure that you are writing your bytes directly at your response! that way your Browser and your Web-Server will know that the download is "in progress",
Example for Java:
Using ServletOutputStream to write very large files in a Java servlet without memory issues
Example for C#:
Writing MemoryStream to Response Object
HOW this 3 steps are built will be up to you, frameworks and libraries you are using, for example Dojo & JQuery have great IFRAME manipulation utilities, all thought you can do the coding by yourself, this is a JQuery sample:
Using jQuery and iFrame to Download a File
Also:
Adding a "busy" animation is ok! you just have to make sure that it's not blocking you'r UI, something like this:
I have an app, that allows users to upload an image, crop it and with other data save it all as html file to be used as a footer for emails. The image is given to them as base64.
Howver turns out this is not supported by Outlook, since it doesnt accept b64 data as img source.
So my idea was to save the cropped image to a file, let's say /public/avatars/avatar.png and link it's directory as a source. However I'm having trouble finding a way how to save images to to a file using JS. My allowed stack is JS and React, no node.js.
How would I do that? I can have the file as either b64 ot canvas element, so i'm flexible here, as long as it's saved to file.
I'm open to other solutions too.
You can't save a file with client language only. You have to save it to a server, own server or external server or a service like AWS.
The best solution without server-side (strangly) is to use a API for save image and get link from this API. Then you can use this link to Outlook.
You can use https://aws.amazon.com/fr/cloudfront/ free for one year with 50Go and 2 millon request monthly.
If you do not exceed 300,000 images per year you can use this service : https://cloudinary.com/pricing
You can also use https://www.deviantart.com/developers/ but that's not really the point of service.
Weird solution :
Be careful, the login and password of your FTP user will be available in the source of your code. Minimum rights must be administered.
You can use this script for talk to FTP server from JS (not tested but seems worked) : http://www.petertorpey.com/files/ae/scripts/FTPConnection.jsx
You can try something like that :
var ftp = new FtpConnection("ftp://URL") ;
ftp.login("username", "password");
ftp.cd("FOLDER") // For move to folder
ftp.put(file,"FILE.PNG") ; // File must be a File JS Object
ftp.close() ;
How to secure the src path of the image when clicks on inspect element so that user should not get to know about the actual src path..please help me with the solution and it should be done with javascript only no other tags should be used.
You can convert image into base 64 data URIs for embedding images.
Use: http://websemantics.co.uk/online_tools/image_to_data_uri_convertor/
Code sample:
.sprite {
background-image:url(data:image/png;base64,iVBORw0KGgoAAAA... etc );
}
This is commonly done server-side, where you have an endpoint that serves the image file to you as bytes...
You can store the images in a private location on the server where IIS/<your favourite web server> doesn't have direct access to it, but only a web app, running on it, with the required privilege is authorized to do so.
Alternatively people also "store" the images in the database itself and load it directly from there.
In either case, the response which has to be sent back has to be a stream of bytes with the correct mime type.
Edit:
Here are a couple of links to get you started if you are into ASP.NET:
http://www.codeproject.com/Articles/34084/Generic-Image-Handler-Using-IHttpHandler
http://aspalliance.com/1322_Displaying_Images_in_ASPNET_Using_HttpHandlers.5 <- this sample actually does it from a database.
Don't let the choice of front-end framework (asp.net, php, django, etc) hinder you. Search for similar techniques in your framework of choice.
Edit:
Another way if you think html5 canvas is shown here: http://www.html5canvastutorials.com/tutorials/html5-canvas-images/
However you run into the same problem. Someone can view the image url if they can see the page source. You'll have to revert to the above approach eventually.
I don't know that this is necessarily important, but I'm using Infragistics iggrid for my grid and their Reports stuff to export to PDF.
The underlying issue I have is that my data that I want to export is in the browser and I would prefer that I don't have to create a server-side file to download. We have an icon on the screen that the user clicks to download the PDF.
So what I'm doing on the client, is collecting all the data. This has to be done client-side because I want to export the data as the user has it sorted, filtered, and column-ordered (otherwise I could just collect the data server-side which would make this simpler). I then send the data to the server via a POST.
On the server-side I generate the PDF file. Now, obviously, I could save the PDF server-side and redirect to the generated file, but that adds maintenance of temporary files which I'd prefer to avoid (but worst case, I can go there. Just fishing for options right now).
I tried returning the data base64 encoded and then doing:
window.open("data:application/pdf;base64," + encodedData);
This doesn't work (at least in IE) because the URL limit is a bit over 2K.
I tried using the downloadDataURI javascript function here: http://code.google.com/p/download-data-uri/
But that only appears to work with Chrome (even after commenting out the webkit check) and I'm apparently not clever enough to figure out why.
I'm sure I'm missing some obvious possibility that doesn't require creating a server-side file, but I'm just not seeing it. (disclaimer: My daughter woke me up horribly early this morning so the answer could be really trivial and I will feel stupid tomorrow when my brain is working).
On the server-side I generate the PDF file. Now, obviously, I could
save the PDF server-side and redirect to the generated file, but that
adds maintenance of temporary files which I'd prefer to avoid (but
worst case, I can go there. Just fishing for options right now).
You don't need to save it on the server. You can simply stream the PDF File (I assume you have it in some sort of Stream or byte[]) to the user. All you need to do is something like
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "filename.pdf");
Response.BinaryWrite(bytes);
Response.Flush();
Response.Close();
Response.End();
And this will prompt the user to either save the file or open it in Adobe Reader. The file won't be created on the server at all.
This is what I want to do:
I want to send an HTTP request to a server, potentially returning a PDF file. But the server may also just return an error code (PDF file unavailable, PDF file invalid, PDF system down, etc). When I get the PDF, I would like to open the PDF and refresh the page that loaded the PDF, because the PDF is then marked as "read". When I get an error code (or timeout), I would like to redirect the page to an error screen. Downloading Google Chrome works in a similar manner:
http://www.google.com/chrome/eula.html?hl=en&platform=win
This is what I don't want do:
For performance reasons, I don't want to issue two requests as suggested in this question here:
Download and open pdf file using Ajax
Two requests can mean:
Make a request for the PDF and return a code to indicate whether the PDF is available or not. If unavailable, immediately display an error page
If it is available, open a window and request the PDF again in that window, and display it.
That's expensive because the PDF's have to be accessed via remote systems. I don't want to access the PDF resource twice. Another solution involving two requests:
Make a request for the PDF and retrieve an error code or a temporary URL where the PDF is cached. On error, immediately display an error page
If the PDF is available, open a window in which the cached PDF is displayed.
This will require for quite a large cache for the PDF's
This might be an interesting lead:
I found this question here giving me some information about how I could download the binary data and make it available in JavaScript as binary data:
Is there a way to read binary data in JavaScript?
Maybe that's a nice lead, but of course it won't solve my problem yet, as I want to use the browser's default editor to open the file, just as if I had requested the file from a normal URL.
So the question is:
Can I download binary data and open them like a regular document from JavaScript? If not, I'll cache the document in some managed memory container in Weblogic and just hope that this won't kill our system. Please only respond:
If you know for sure it cannot be done (some links explaining why would be nice)
If you know how to do it
If you have a different solution doing roughly what I want to do (not issuing two requests)
The implemented "old-school" solution works like this:
The JavaScript client sends an AJAX request to the server to "prepare" a PDF document
The server responds with any of these three messages:
a) Document available at URL http://www.example.com/doc.pdf
b) Document unavailable
c) Document being "prepared" (i.e. client has to wait)
The JavaScript client then reacts as such:
a) Open the returned URL in a new window, refresh the current window after 5 seconds
b) The current window is redirected to an error screen
c) The current window stays unchanged and AJAX polling is implemented to repeat step 2