I recently published a Sitecore Marketplace module PDF Thumbnail Creater Handler. Basically it allows to generate thumbnail on-the-fly (dynamically) for the uploaded PDF in sitecore by passing width and/or height. This will allow user to request thumbnail for any height or width and the thumbnail will be stored as a media cache in Sitecore.
Below are the outputs of same PDF but different size thumbnails:
So, what I wanted to achieve:
You can read my older post regarding Sitecore Custom HTTP Handler, I have described there in detail.
I created own Sitecore Custom Handlers (SitecoreTactics.ThumbnailManager.PDFThumbnailRequestHandler) to generate thumbnails of media(PDF) items. See below config changes this requires:
You can get full source code (of older version) of this module from Sitecore Marketplace.
Related Posts:
- Show PDF Thumbnail Icons in Content Editor
- Sitecore HTTP Custom Handler
Requirement
Suppose user uploaded PDF in Sitecore.- User want to generate thumbnails of the fist page of uploaded PDF.
- User can choose height and/or width of thumbnails without any configurations.
- If user replaces a new file instead of that PDF, it should serve thumbnail of newly uploaded PDF.
- Similarly the thumbnail URL should work if user moves/copies/deletes the PDF item.
- Finally, the conversion process should be scalable and quick enough so it does not affect performance and the thumbnails should be cached as media cache too
Below are the outputs of same PDF but different size thumbnails:
PDF Thumb Original http://sitecore/~/mediathumb/Files/MyPDF.pdf |
PDF Thumb Width=300 http://sitecore/~/mediathumb/Files/MyPDF.pdf?w=300 |
PDF Thumb Width=150 http://sitecore/~/mediathumb/Files/MyPDF.pdf?w=150 |
---|---|---|
Theoretical Concept
I just thought to achieve this overriding Sitecore MediaRequestHandler. Sitecore allows to generate different sized thumbnails of uploaded image files, See how. Why should not I use the same concept to generate thumbnails from PDF? Only concern I looked was to convert PDF to JPG only, but was not that much easy.So, what I wanted to achieve:
PDF / Thumbnail Details | PDF / Thumbnail Path/URL |
---|---|
PDF Sitecore Path | /sitecore/media library/Files/MyPDF |
PDF URL | http://sitecore/~/media/Files/MyPDF.pdf |
PDF Thumbnail URL | http://sitecore/~/mediathumb/Files/MyPDF.pdf or http://sitecore/~/mediathumb/Files/MyPDF.jpg |
PDF Thumbnail URL Width = 100 px |
http://sitecore/~/mediathumb/Files/MyPDF.pdf?w=100 |
PDF Thumbnail URL Height = 200 px |
http://sitecore/~/mediathumb/Files/MyPDF.pdf?h=200 |
PDF Thumbnail URL Width = 100 px Height = 200 px |
http://sitecore/~/mediathumb/Files/MyPDF.pdf?w=100&h=200 |
How this achieved
PDF to JPG conversion can be done using GhostScript (With GPL License, which is free), which is very efficient and gives flexibility with many other options.You can read my older post regarding Sitecore Custom HTTP Handler, I have described there in detail.
I created own Sitecore Custom Handlers (SitecoreTactics.ThumbnailManager.PDFThumbnailRequestHandler) to generate thumbnails of media(PDF) items. See below config changes this requires:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <!-- Define Custom Handler --> <customHandlers> <handler trigger="~/mediathumb/" handler="sitecore_media_thumb.ashx" /> </customHandlers> <!-- Define Media Prefix --> <mediaLibrary> <mediaPrefixes> <prefix value="~/mediathumb" /> </mediaPrefixes> </mediaLibrary> </sitecore> <!-- Define Web Handler --> <system.webServer> <handlers> <add verb="*" path="sitecore_media_thumb.ashx" type="SitecoreTactics.ThumbnailManager.PDFThumbnailRequestHandler, SitecoreTactics.ThumbnailManager" name="SitecoreTactics.PDFThumbnailRequestHandler"/> </handlers> </system.webServer> </configuration>Handler's source code to process thumbnail and use media cache as below.
namespace SitecoreTactics.ThumbnailManager { public class PDFThumbnailRequestHandler : Sitecore.Resources.Media.MediaRequestHandler { protected override bool DoProcessRequest(HttpContext context) { Assert.ArgumentNotNull(context, "context"); MediaRequest request = MediaManager.ParseMediaRequest(context.Request); if (request == null) return false; Sitecore.Resources.Media.Media media = null; try { media = MediaManager.GetMedia(request.MediaUri); if (media != null) return this.DoProcessRequest(context, request, media); } catch (Exception ex) { Log.Error("PDF Thumbnail Generator error - URL:" + context.Request.Url.ToString() + ". Exception:" + ex.ToString(), this); } if (media == null) { context.Response.Write("404 - File not found"); context.Response.End(); } else { string itemNotFoundUrl = (Context.Site.LoginPage != string.Empty) ? Context.Site.LoginPage : Settings.NoAccessUrl; if (Settings.RequestErrors.UseServerSideRedirect) HttpContext.Current.Server.Transfer(itemNotFoundUrl); else HttpContext.Current.Response.Redirect(itemNotFoundUrl); } return true; } protected override bool DoProcessRequest(HttpContext context, MediaRequest request, Sitecore.Resources.Media.Media media) { Assert.ArgumentNotNull(context, "context"); Assert.ArgumentNotNull(request, "request"); Assert.ArgumentNotNull(media, "media"); if (this.Modified(context, media, request.Options) == Sitecore.Tristate.False) { Event.RaiseEvent("media:request", new object[] { request }); this.SendMediaHeaders(media, context); context.Response.StatusCode = 0x130; return true; } // Gets media stream for the requested media item thumbnail MediaStream stream = ProcessThumbnail(request, media); if (stream == null) { return false; } Event.RaiseEvent("media:request", new object[] { request }); this.SendMediaHeaders(media, context); this.SendStreamHeaders(stream, context); using (stream) { context.Response.AddHeader("Content-Length", stream.Stream.Length.ToString()); WebUtil.TransmitStream(stream.Stream, context.Response, Settings.Media.StreamBufferSize); } return true; } private MediaStream ProcessThumbnail(MediaRequest request, Sitecore.Resources.Media.Media media) { MediaStream mStream = null; ParseQueryString(request); mStream = MediaManager.Cache.GetStream(media, request.Options); if (mStream == null) { string tempPath = Settings.TempFolderPath + "/PDF-Thumbnails/"; tempPath = MainUtil.MapPath(tempPath); if (!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); // Prepare filenames string pdfFile = tempPath + media.MediaData.MediaId + ".pdf"; string jpgFile = tempPath + media.MediaData.MediaId + ".jpg"; string resizedJpgFile = tempPath + media.MediaData.MediaId + "_" + request.Options.Width.ToString() + "_" + request.Options.Height.ToString(); if (!File.Exists(jpgFile)) { // Save BLOB media file to disk MediaConverter.ConvertMediaItemToFile(media.MediaData.MediaItem, pdfFile); // Convert PDF to Jpeg - First Pager MediaConverter.ConvertPDFtoJPG(pdfFile, 1, jpgFile); } // Resize Image MediaConverter.ReSizeJPG(jpgFile, resizedJpgFile, request.Options.Width, request.Options.Height, true); // Convert resized image to stream MediaStream resizedStream = new MediaStream(File.Open(resizedJpgFile, FileMode.Open, FileAccess.Read, FileShare.Read), "jpg", media.MediaData.MediaItem); // Add the requested thumbnail to Media Cache MediaStream outStream = null; MediaManager.Cache.AddStream(media, request.Options, resizedStream, out outStream); if (outStream != null) { // If Media cache is enabled return outStream; } } // If Media cache is disabled return mStream; } public void ParseQueryString(MediaRequest mediaRequest) { HttpRequest httpRequest = mediaRequest.InnerRequest; Assert.ArgumentNotNull((object)httpRequest, "httpRequest"); string str1 = httpRequest.QueryString["as"]; if (!string.IsNullOrEmpty(str1)) mediaRequest.Options.AllowStretch = MainUtil.GetBool(str1, false); string color = httpRequest.QueryString["bc"]; if (!string.IsNullOrEmpty(color)) mediaRequest.Options.BackgroundColor = MainUtil.StringToColor(color); string str2 = httpRequest.QueryString["dmc"]; mediaRequest.Options.Height = MainUtil.GetInt(httpRequest.QueryString["h"], 0); string str3 = httpRequest.QueryString["iar"]; if (!string.IsNullOrEmpty(str3)) mediaRequest.Options.IgnoreAspectRatio = MainUtil.GetBool(str3, false); mediaRequest.Options.MaxHeight = MainUtil.GetInt(httpRequest.QueryString["mh"], 0); mediaRequest.Options.MaxWidth = MainUtil.GetInt(httpRequest.QueryString["mw"], 0); mediaRequest.Options.Scale = MainUtil.GetFloat(httpRequest.QueryString["sc"], 0.0f); string str4 = httpRequest.QueryString["thn"]; if (!string.IsNullOrEmpty(str4)) mediaRequest.Options.Thumbnail = MainUtil.GetBool(str4, false); mediaRequest.Options.Width = MainUtil.GetInt(httpRequest.QueryString["w"], 0); } } }
You can get full source code (of older version) of this module from Sitecore Marketplace.
Update:
Module available on Sitecore Marketplace contains older code, having a bug on media cache that when someone overwrite media files (using detach/attach), it was serving older thumbnail. This bug has been fixed in above code, and will be available on marketplace very soon. Meanwhile, you can download the source code (excluding DLLs) from https://drive.google.com/file/d/0B1otw7vE3rGTQmU1U1l2TTJHQTA/view?usp=sharing:
Module available on Sitecore Marketplace contains older code, having a bug on media cache that when someone overwrite media files (using detach/attach), it was serving older thumbnail. This bug has been fixed in above code, and will be available on marketplace very soon. Meanwhile, you can download the source code (excluding DLLs) from https://drive.google.com/file/d/0B1otw7vE3rGTQmU1U1l2TTJHQTA/view?usp=sharing:
Benefits of this approach
- Dynamic conversion of PDF to Thumbnail when requested
- Allows to convert different size thumbnails
- Repeated thumbnails will be served from media cache.
- Conversion is fast using GhostScript and media cache adds more power.
Related Posts:
- Show PDF Thumbnail Icons in Content Editor
- Sitecore HTTP Custom Handler
One major benefit of this module is, even when you attach or upload a new PDF file in same media item, it will generate thumbnail of latest PDF. So no worries on renaming, updating, moving the PDF media item.
ReplyDeleteI tried same code but i got error like "Permission to the requested document was denied". Have you any idea why it not generate thumbnail?
ReplyDeleteHardik, along with the code, you need to do the config settings. You can also download this module directly from sitecore marketplace, would be easy to configure. Url: http://marketplace.sitecore.net/en/Modules/PDF_Thumbnail_Handler.aspx
DeleteIf still you have any doubt, please reach me through email, will be happy to help you.
Hi yogesh
ReplyDeleteI have got issue like "Permission to the requested document was denied".
When I was uploading some PDF in Sitecore admin then PDF is uploaded successfully and then I tried to access PDF in website that time thumbnail not generated it show broken image Icon.when I inspected the website and I found that 403 Permission Denied issue. Other old PDF thumbnail rendering perfectly
When I deleted all media catch under APP_DATA folder and I have reset IIS then all thumbnails rendered perfectly.
can you provide me proper solution of above issue?.
Hi Pravin, I have not come across the 403 issue, will try to reproduce it.
DeleteBut there was a bug in this module is - thumbnail generation after overwriting the media item. It's fixed now, and you can download it from above updated code.
If this does not fix this issue, please email me with more details, will surely look into it. Thank you for reading and updating raising issue.