عنوان:

‫نمایش یک فایل PDF پویا در یک iframe در ASP.NET


نویسنده: وحید نصیری
تاریخ: ۱۳۹۱/۱۲/۲۸ ۱۱:۲۵
آدرس: www.dntips.ir
عموما در برنامه‌های وب برای نمایش فایل‌های پویای باینری تولید شده، یا ابتدا آن‌ها را بر روی سخت دیسک ذخیره کرده و مسیر نهایی را به نحوی به کاربر نمایش می‌دهند و یا فایل را بدون ذخیره سازی، در مرورگر کاربر اصطلاحا Flush می‌کنند. حالت Flush سبب نمایش صفحه دیالوگ ذخیره سازی فایل گردیده و در همینجا Response خاتمه خواهد یافت.
برای نمونه در اینجا توسط متد inMemoryFile، یک فایل PDF در حافظه تشکیل شده و سپس به صورت یک Byte Array بازگشت داده می‌شود. در ادامه کار، این اطلاعات در مرورگر کاربر Flush خواهد شد:
using System.IO;
using System.Net.Mime;
using System.Web;

namespace WebApplication
{
    public class PdfHandler : IHttpHandler
    {
        private static byte[] inMemoryFile()
        {
            //تولید پویای فایل در حافظه و یا حتی خواندن از یک نمونه موجود
            return File.ReadAllBytes(@"D:\path\DynamicCrosstabSampleRpt.pdf");
        }

        public void ProcessRequest(HttpContext context)
        {
            var pdf = inMemoryFile();

            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.ContentType = MediaTypeNames.Application.Pdf;
            context.Response.AddHeader("Content-Length", pdf.Length.ToString());
            context.Response.AddHeader("content-disposition", "attachment;filename=test.pdf");
            context.Response.Buffer = true;
            context.Response.Clear();
            context.Response.OutputStream.Write(pdf, 0, pdf.Length);
            context.Response.OutputStream.Flush();
            context.Response.OutputStream.Close();
            context.Response.End();
        }

        public bool IsReusable
        {
            get  {  return false;  }
        }
    }
}
و برای نمایش آن در یک iframe در صفحه:
 <iframe width="100%" src="PdfHandler.ashx" height="200px"></iframe>
نتیجه کار، نمایش صفحه دیالوگ ذخیره سازی فایل به کاربر است:


سؤال: فرض کنید Adobe reader بر روی سیستم نصب است و مرورگر با استفاده از Active-X آن می‌تواند این نوع فایل‌ها را نمایش دهد. آیا راهی وجود دارد تا بجای نمایش save popup dialog، این فایل توسط مرورگر نمایش داده شود؟
پاسخ: بلی. در کدهای فوق تنها کافی است یک سطر آن تغییر کند:
 Response.AddHeader("content-disposition", "inline;filename=test.pdf");
در اینجا تنها نحوه مقدار دهی content-disposition تفاوت کرده است. حالت attachment سبب نمایش save popup dialog می‌شود و مقدار inline، فایل را در مرورگر نمایش خواهد داد.
اینبار اگر برنامه را اجرا کنیم، iframe ایی که به PdfHandler.ashx اشاره می‌کند، فایل PDF را در صفحه نمایش می‌دهد.

نظرات

  • مهمان در ۱۳۹۱/۱۲/۲۸ ۱۱:۴۸
    عالی بود مهندس. در صورتی که inline باشه ولی برنامه ای برای استفاده از پی دی اف نباشه یا مرورگر مجاز به استفاده نباشه باز صفحه دانلود دیده میشه؟
    • وحید نصیری در ۱۳۹۱/۱۲/۲۸ ۱۲:۱۲
      بله. صفحه file download فوق باز می‌شود.
  • امیر هاشم زاده در ۱۳۹۳/۰۵/۲۰ ۲۲:۱۴
    آیا امکان عدم دریافت فایل مذکور توسط دانلود منیجرها وجود دارد؟
    • وحید نصیری در ۱۳۹۳/۰۵/۲۰ ۲۲:۱۹
      با یک پسوند دیگر که نمی‌شناسند تست کنید؛ مثلا filename=test.abc 
      • افتابی در ۱۳۹۳/۰۵/۳۱ ۰:۱۶
        متشکر؛ خوب اگه من بخوام با استفاده از PDFReport گزارش درست کنم و توی خود view نمایش بدم ، این کدها رو توی کدوم کلاس بگذارم؟
        • وحید نصیری در ۱۳۹۳/۰۵/۳۱ ۰:۲۴
          به صورت توکار لحاظ شده:
           .Generate(data =>
                          {
                              fileName = HttpUtility.UrlEncode(fileName, Encoding.UTF8);
                              data.FlushInBrowser(fileName, FlushType.Inline);
                          }); // creating an in-memory PDF file
          FlushType.Inline آن همان مطلب جاری است.
  • توکلی در ۱۳۹۴/۱۲/۰۲ ۲۲:۳
    با تشکر ، من یه ftp handler با استفاده از مقاله شما ایجاد کردم ولی سرعت خیلی پائینی تو دانلود دارم میشه راهنمائی کنید دلیلش چیه ؟

        public void ProcessRequest (HttpContext context) {
    
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("serverpath" + "filename");
            request.UsePassive = false;
            request.Credentials = new NetworkCredential("user", "pass");
    
          //  byte[] fileBytes = null;
    
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                    context.Response.ContentType = MediaTypeNames.Application.Pdf;
                    context.Response.AddHeader("content-disposition", "attachment; filename=test.pdf");
                    context.Response.BufferOutput = false;   // to prevent buffering 
                    //context.Response.Buffer = true;
                    context.Response.Clear();
    
                    byte[] buffer = new byte[262144];
                    int bytesRead = 0;
                    while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        context.Response.OutputStream.Write(buffer, 0, bytesRead);
                    }
                    context.Response.OutputStream.Flush();
                    context.Response.OutputStream.Close();
                    context.Response.End();
                }
            }
    
            request.Abort();
            context.ApplicationInstance.CompleteRequest(); 
        }
    • وحید نصیری در ۱۳۹۴/۱۲/۰۲ ۲۲:۵۱
      در حالت معمولی نوشتن در Response، برنامه‌های دانلود منیجر امکان باز کردن چندین ترد را نخواهند داشت؛ چون مفهوم Range: bytes را پیاده سازی نکرده‌اید (به همین جهت کند به نظر می‌رسد). یک نمونه پیاده سازی این مفهوم برای ASP.NET MVC در اینجا مطرح شده‌است (نکات آن عمومی است و سورس آن‌را بررسی کنید قابل استفاده‌ی در وب فرم‌ها هم هست).
  • مهدی ملائیان در ۱۳۹۵/۱۱/۰۵ ۰:۴۹
    باتشکر. من در حالت اول که تنظیمات فایل بصورت Attachment  هست می‌تونم براحتی دانلود کنم فایل pdf رو ولی در حالت inline در iframe چیزی نمایش داده نمیشه. همه موارد و نکات رو هم رعایت کردم.

    • وحید نصیری در ۱۳۹۵/۱۱/۰۵ ۱:۴۱
      در تصویر شما حجم فایل «ناعدد» ذکر شده. این فایل قابل گشودن هست؟
      • مهدی ملائیان در ۱۳۹۵/۱۱/۰۵ ۱:۵۹
        بله. فایل رو خارج از مرورگر باز می‌کنم بعد از دانلود .
        البته از طریق تنظیمات فایرفاکس گزینه  Restart with add-ons disabled رو اجرا کردم. مشکل حل شد.

        • مهدی ملائیان در ۱۳۹۵/۱۱/۰۵ ۱۰:۱۳
          تا زمانی که بصورت لوکال دارم تست می‌کنم فایل درون مرورگر نمایش داده میشه ولی وقتی پابلیش میشه بر روی یک سرور مجدد یا دانلود منیجر فعال میشه  و یا خود مرورگر گزینه ذخیره سازیش فعال میشه.آیا راهی وجود داره که مشکل رو بصورت کامل حل کنم.
          • وحید نصیری در ۱۳۹۵/۱۱/۰۵ ۱۰:۴۶
            خلاصه‌ی نظرات بحث جاری:
            - اگر برنامه‌ی download manager ایی نصب است که پسوندهای pdf را به صورت خودکار شناسایی و دریافت می‌کند، کار به نمایش فایل PDF نخواهد رسید. پسوند PDF را از لیست قابل شناسایی آن‌ها حذف کنید.
            - اگر Active-X مربوط به Adobe Reader بر روی سیستم کلاینت نصب نباشد، این روش با تمام مرورگرها کار نخواهد کرد؛ چون تمام آن‌ها PDF Reader ندارند (مانند کروم و یا فایرفاکس).
            - اگر از کتابخانه‌ی PDF Report استفاده می‌کنید، متد data.FlushInBrowser(fileName, FlushType.Inline) نکات بحث فوق را دارد.
            - روش دیگر اینکار (نمایش فایل PDF در مرورگر) استفاده از Response.Redirect است به آدرس فایل PDF بر روی سرور. اگر مرورگر PDF Reader و یا Active-X مربوطه را داشته باشد و همچنین برنامه‌ی download manager ایی هم مزاحمت ایجاد نکند، فایل PDF در مرورگر نمایش داده خواهد شد (البته در یک صفحه‌ی جدید).
            - با استفاده از افزونه‌ی pdf.js هم می‌توان فایل‌های PDF را رندر کرد (یک مثال و مثالی دیگر).