<script> $( "#get-scan" ).click(function () { var url = 'http://localhost:8010/' ; $. get (url, function (data) { $( "#img-scanned" ).attr( "src" , "data:image/Jpeg;base64, " + data.GetScanResult); }); });
چه نوع اپلیکیشنهای را میتوان با Node.js توسعه داد؟
- سرور WebSocket جهت توسعهی اپلیکیشنهای بلادرنگ
- فایل آپلودر سریع در سمت کلاینت
- Ad Server
- و ...
var contents = fs.readFileSync('filePath'); console.log(content); console.log('Doing something else');
fs.readFile('filePath', function (err, contents) { console.log(contents); }); console.log('Doing something else');
برای شروع به کار با Node.js میتوانید با مراجعه به وبسایت رسمی آن، آنرا دانلود و بر روی سیستم خود نصب کنید. بعد از نصب Node میتوانیم از طریق command line وارد shell آن شوید و دستورات جاوا اسکریپتی خود را اجرا نمائید:
احتمالاً به این نوع استفادهی از Node.js که به REPL معروف است، نیازی نداشته باشید. در واقع هدف بررسی نصب بودن رانتایم بر روی سیستم است. با استفاده از فرمان node نیز میتوان یک فایل جاوا اسکریپتی را اجرا کرد. برای اینکار یک فایل با نام test.js را با محتویات زیر درون VS Code ایجاد کنید:
سپس دستور node test.js را وارد کنید:
همانطور که مشاهده میکنید نتیجهی فایل عنوان شده، در خروجی نمایش داده شده است. در حالت کلی تمام کاری که نود انجام میدهد، ارائه یک Execution engine برای جاوا اسکریپت میباشد.
استفاده از Node.js در ویژوال استودیو
برای کار با Node.js درون ویژوال استودیو باید ابتدا افزونهی Node.js Tools را برای ویژوال استودیو نصب کنید. بعد از نصب این افزونه، تمپلیت Node.js در زمان ایجاد یک پروژه برای شما نمایش داده خواهد شد:
برای شروع، تمپلیت Blank Node.js Console Application را انتخاب کرده و بر روی OK کلیک کنید. با اینکار یک پروژه با ساختار زیر برایمان ایجاد خواهد شد:
همانطور که ملاحظه میکنید، یک فایل با نام app.js درون تمپلیت ایجاد شده، موجود است. app.js در واقع نقطهی شروع برنامهمان خواهد بود. همچنین دو فایل دیگر نیز با نامهای README.md، جهت افزودن توضیحات و یک فایل با نام package.json، جهت مدیریت وابستگیهای برنامه به پروژه اضافه شدهاند. اکنون میتوانیم شروع به توسعهی برنامهی خود درون ویژوال استودیو کنیم. همچنین میتوانیم از قابلیتهای debugging ویژوال استودیو نیز بهره ببریم:
اگر مسیر پروژهی ایجاد شدهی فوق را درون windows explorer باز کنید خواهید دید که ساختار آن شبیه به یک پروژهی Node.js میباشد. با این تفاوت که دو آیتم دیگر همانند دیگر پروژههای ویژوال استودیو نیز به آن اضافه شده است که طبیعتاً میتوانید در حین کار با سورس کنترل، از انتشار آنها صرفنظر کنید.
لازم به ذکر است پروژهی ایجاد شدهی فوق را نیز میتوانید همانند حالت عادی، از طریق command line و همانند پروژههای Node.js اجرا کنید:
node app.js
در واقع از ویژوال استدیو میتوانیم به عنوان یک ابزار برای دیباگ پروژههای Node.js استفاده کنیم. لازم به ذکر است، Visual Studio Code نیز امکان دیباگ اپلیکیشنهای Node.js را در اختیارمان قرار میدهد. در نتیجه در مواقعیکه نسخهی کامل ویژوال استودیو در دسترس نیست نیز میتوانیم از VS Code برای دیباگ برنامههایمان استفاده کنیم:
معرفی کتاب: مرجع کامل ASP.NET MVC 4
تبریک میگم. حقیقتا از خواندن کتابهای شما لذت میبرم.
راستش فکر میکنم اگر در مورد EF Code First هم یک کتاب بنویسید ، شاهکار خواهید کرد.
من تمام کتابهای شما را خوانده ام. بی صبرانه منتظر کتابهای آینده شما هستم.
با تشکر
موفق و سربلند باشید.
کاربران امروزه با عناصری که به نحوی خاص درون صفحه عمل میکنند، آشنا شدهاند. بنابراین انتخاب مناسب برای اتخاذ این عناصر زمانی مناسب است که به تکمیل کارآیی و رضایت کاربر کمک کند.
یک کامپوننت در واقع از دو بخش تشکیل شدهاست:
1 - اول اینکه چگونه به نظر میرسد ( UI ).
2 – دوم اینکه چگونه کار میکند ( UX ).
این عناصر رابط ( component ) شامل :
Input Controls : check boxes, radio buttons, drop down lists, list boxes, buttons, toggles, text fields, date field
Navigational Components : breadcrumb, slider, search field, pagination, slider, tags, icons
Informational Components : tool tips, icons, progress bar, notifications, message boxes, modal windows
Containers : accordion
اما باید توجه داشت که فقط به این موارد محدود نمیشوند.
در این
قسمت به طور مختصر با این دست از کامپوننتها ( UI Component ) آشنا میشویم.
کامپوننت V-Alert برای انتقال اطلاعات مهم به کاربر مورد استفاده قرار میگیرد. این کامپوننت چهار نوع اطلاعات را به کاربر گوشزد میکند که شامل موفقیتها، اطلاعات، هشدارها و خطاها میباشد.
هشدارها میتوانند یک رنگ خاص را داشته باشند که به طور پیش فرض نمایش داده نمیشوند.
در مثال پایین، کامپوننت v-alert شامل دو مقدار است که برای آن تنظیم شدهاست. مقدار (value) که شامل یک مقدار Boolean است و مقدار (type) که مشخص کننده نوع هشدار است (موفقیت ، اطلاعات ، هشدار و خطا).
در قطعه کد پایین، این چهار نوع اطلاعات قابل نمایش به کاربر مشخص شدهاند:
<div id="app"> <v-app id="inspire"> <div> <v-alert :value="true" type="success"> This is a success alert. </v-alert> <v-alert :value="true" type="info"> This is a info alert. </v-alert> <v-alert :value="true" type="warning"> This is a warning alert. </v-alert> <v-alert :value="true" type="error"> This is a error alert. </v-alert> </div> </v-app> </div>
برای کامپوننت V-Alert میتوان propertiesهای مختلفی را مشخص نمود که از جمله آنها میتوان به موارد زیر اشاره کرد:
Color : به وسیله این property میتوان رنگ پیغام را مشخص نمود. هم به وسیله نام رنگ میتوان رنگ مورد نظر را مشخص کرد و هم به وسیلهی کد RGB این کار را انجام داد. dismissible : این تنظیم مشخص میکند که پیغام، قابلیت بسته شدن را دارد یا خیر که حاوی یک مقدار Boolean است.
icon : مشخص کننده یک نماد خاص است که درون جعبه پیغام قرار میگیرد.
type : مشخص کننده نوع پیام است که پیشتر در مورد آن توضیح داده شد.
کامپوننت v-avatar برای تغییر اندازه تصاویر مورد استفاده قرار میگیرد که معمولا جهت نمایش عکس پروفایل استفاده میشود.
طریقه استفاده :
avatar، دارای یک اندازهی پویا است که میتواند برای هر وضعیتی تغییر کند.
برای این کامپوننت سه properties قابل تنظیم است:
color : به وسیله این property میتوان رنگ دلخواهی را برای آواتار مشخص نمود. هم به وسیله نام رنگ میتوان رنگ مورد نظر را مشخص کرد و هم به وسیله کد RGB این کار را صورت داد.
size : به طور پیشفرض برای avatar، سایز 48 تنظیم شدهاست که میتوان این میزان را کم و یا زیاد کرد.
tile : همانند border radius در css عمل میکند. با تنظیم این گزینه میتوانیم یک آواتار گرد داشته باشیم.
به وسیله این کامپوننت میتوان نمادهایی را برای نمایش اطلاعاتی به کاربر یا جلب توجه کاربر به یک عنصر خاص، ایجاد نمود.
این کامپوننت نیز properties خاص خود را دارد که از جمله آن میتوان به color , left , mode , overlab و غیره اشاره کرد.
قطعه کد پایین نشان دهنده چگونگی عملکرد این کامپوننت است:
<div id="app"> <v-app id="inspire"> <div> <v-badge color="purple" left overlap> <template v-slot:badge> <v-icon dark small> done </v-icon> </template> <v-icon color="grey lighten-1" large> account_circle </v-icon> </v-badge> <v-badge overlap color="orange"> <template v-slot:badge> <v-icon dark small> notifications </v-icon> </template> <v-icon large color="grey darken-1"> account_box </v-icon> </v-badge> </div> </v-app> </div>
نتیجه قطعه کد بالا بدین ترتیب است:
این کامپوننت را میتوان جایگزین sidebarها نمود. این کامپوننت در درجه اول در موبایل مورد استفاده قرار میگیرد که میتواند شامل متن و یا آیکن باشد.
به وسیله این کامپوننت امکان انتقال از یک بخش از صفحه به بخشی دیگر امکان پذیر میشود.
این کامپوننت نیز properties خاص خود را دارد که از جمله آن میتوان به active-sync (برای نشان دادن فعال یا غیر فعال بودن گزینه انتخاب شده)، fixed ( برای مشخص کردن موقعیت کامپوننت در صفحه) و موارد دیگر اشاره کرد.
تقسیم بندی اجزاء این کامپوننت به شرح زیر است:
1 - محل قرار گیری کامپوننت
2- آیکن غیر فعال
3- برچسب غیر فعال
4 - آیکن فعال
5- برچسب فعال
قطعه کد پایین نشان دهنده چگونگی یک bottom navbar است:
<div id="app"> <v-app id="inspire"> <v-card height="200px" flat> <div> Active: {{ bottomNav }} // </div> <v-bottom-nav :active.sync="bottomNav" :value="true" absolute color="transparent"> <v-btn color="teal" flat value="recent"> <span>Recent</span> <v-icon>history</v-icon> </v-btn> <v-btn color="teal" flat value="favorites"> <span>Favorites</span> <v-icon>favorite</v-icon> </v-btn> <v-btn color="teal" flat value="nearby"> <span>Nearby</span> <v-icon>place</v-icon> </v-btn> </v-bottom-nav> </v-card> </v-app> </div>
این کامپوننت برای ایجاد یک دکمه چه به صورت متن و یا آیکن مورد استفاده قرار میگیرد. دکمهها به کاربران این امکان را میدهند تا اقداماتی را انجام دهند و انتخابهای خود را تنها با یک کلیک انجام دهند.
از دکمهها ممکن است در جاهای مختلف صفحه به خصوص در دیالوگ باکسها، فرمها و ابزارها مورد استفاده قرار گیرد.
کامپوننت v-btn نیز مانند سایر کامپوننتها تنظیمات خاص خود را دارد که از جمله آن میتوان به کوچکی و بزرگی دکمه، فعال یا غیر فعال بودن دکمه، نوع متن یا آیکن بودن دکمه اشاره نمود.
حالتهای مختلفی از دکمهها وجود دارند که میتوانند به بهتر شدن UI برنامه ما کمک کنند. برای مثال میتوان به موارد زیر اشاره کرد:
button drop-down variants : دکمههای کرکرهای که معمولا برای نظم و کم جا بودن در صفحه مورد استفاده قرار میگیرند.
icons : آیکنها میتوانند برای محتوای اصلی یک دکمه مورد استفاده قرار بگیرند تا ظاهر زیباتری را به دکمه ما بدهند.
floating : این دکمهها حالت آیکن را دارند؛ با این تفاوت که آیکن مورد نظر، درون یک محتوا قرار میگیرد.
loaders : به وسیله این دکمهها میتوان کاربر را متوجه انجام یک پردازش نمود. به صورت پیشفرض بعد از فشردن این نوع دکمهها محتوای دکمه فشرده شده تغییر ظاهر داده و به شکل یک دایره در حال چرخش در میآید. البته میتوان این پیشفرض را به حالتهای دیگری نیز تغییر داد.
round : این نوع دکمهها دقیقا کارآیی دکمههای معمولی را دارند؛ با این تفاوت که این دکمهها دارای لبههایی گرد هستند.
یک نمونه از ایجاد انواع دکمهها در زیر آمده است:
<div id="app"> <v-app id="inspire"> <div> <v-btn color="success">Success</v-btn> <v-btn color="error">Error</v-btn> <v-btn color="warning">Warning</v-btn> <v-btn color="info">Info</v-btn> </div> </v-app> </div>
یکی از کامپوننتهایی که به تازگی به vuetify اضافه شده است، کامپوننت تقویم یا v-calendar است. از این کامپوننت برای نمایش تاریخ، روز، هفته، ماه و سال استفاده میشود. یک تقویم دارای یک نوع و یک مقدار است که تعیین میکند چه نوع تقویمی، در طول چه مدت زمانی نمایش داده شود.
حالتهای مختلفی برای نمایش تقویم در صفحه وجود دارد که برای مثال میتوان به موارد زیر اشاره کرد:
events : به وسیله این گزینه میتوان برای هر روز یک رخداد خاص را مشخص نمود که به وسیله کلیک بر روی آن، اطلاعات آن رخداد نمایش داده شود.
weekly : میتوان یک تقویم هفتگی را ایجاد نمود و رخدادهای هفتگی را برای آن تنظیم کرد.
نمونه ایجاد یک تقویم در پایین آمده است:
<div id="app"> <v-app id="inspire"> <v-layout wrap> <v-flex xs12> <v-sheet height="500"> <v-calendar ref="calendar" v-model="start" :type="type" :end="end" color="primary"> </v-calendar> </v-sheet> </v-flex> <v-flex sm4 xs12> <v-btn @click="$refs.calendar.prev()"> <v-icon dark left> keyboard_arrow_left </v-icon> Prev </v-btn> </v-flex> <v-flex sm4 xs12> <v-select v-model="type" :items="typeOptions" label="Type"> </v-select> </v-flex> <v-flex sm4 xs12> <v-btn @click="$refs.calendar.next()"> Next <v-icon right dark> keyboard_arrow_right </v-icon> </v-btn> </v-flex> </v-layout> </v-app> </div>
js قطعه کد new Vue({ el: '#app', data: () => ({ type: 'month', //مشخص کننده نوع تقویم که در اینجا تقویم به صورت ماهانه است start: '2019-01-01', end: '2019-01-06', typeOptions: [ { text: 'Day', value: 'day' }, { text: '4 Day', value: '4day' }, { text: 'Week', value: 'week' }, { text: 'Month', value: 'month' }, { text: 'Custom Daily', value: 'custom-daily' }, { text: 'Custom Weekly', value: 'custom-weekly' } ] }) })
کار با Kendo UI DataSource
<script type="text/javascript"> $(function () { // var r = "12"; var productsDataSource = new kendo.data.DataSource({ transport: { read: { url: "@Url.Action("GetProducts", "Home")", dataType: "json", contentType: 'application/json; charset=utf-8', type: 'GET', data: { param1: "dfvdf", param2: "val2" } // ارسال اطلاعات اضافی و سفارشی به سرور در حین درخواست }, create: { url: "@Url.Action("PostProduct","Home")", contentType: 'application/json; charset=utf-8', type: "POST" }, update: { url:// function (product) { "@Url.Action("UpdateProduct","Home")",//, +product.Id; //}, contentType: 'application/json; charset=utf-8', type: "PUT" }, destroy: { url: function (p) { return "@Url.Action("DeleteProduct","Home")/" + p.Id; }, contentType: 'application/json; charset=utf-8', type: "DELETE" }, parameterMap: function (options) { return kendo.stringify(options); } }, schema: { parse: function (data) { return data; }, data: "Data", total: "Total", model: { id: "Id", // define the model of the data source. Required for validation and property types. fields: { "Id": { type: "number", editable: false }, //تعیین نوع فیلد برای جستجوی پویا مهم است "Name": { type: "string", validation: { required: true }, editable: true }, "Discription": { type: "string", }, "Title": { type: "string", editable: false }, "GroupName": { type: "string", }, "Link": { type: "string" } } }, batch: false, }, error: function (e) { alert(e.errorThrown.stack); }, pageSize: 5, sort: { field: "Id", dir: "desc" } }); $("#report-grid").kendoGrid({ dataSource: productsDataSource, autoBind: true, scrollable: false, pageable: true, sortable: true, columns: [ { field: "Id", title: "#" }, { field: "Name", title: "Product" } ] }); }); </script>
ما مطابق آموزشی که در این مقاله داده شده از یک اکشن متد برای ذخیره عکس ارسالی تو یک پوشه و سپس برگشت دادن مسیر عکس و از یک اکشن متد دیگه برای ذخیره اطلاعاتی که قراره همراه با فرم ارسال بشن (به همراه مسیر عکس برگشت داده شده)، استفاده میکنیم
مشکلی که ما موقع استفاده از این افزونه باهاش برخوردیم اینه که گاهی اوقات و همونطور که انتظار میره اکشن متد (AddAvatars) که وظیفه ذخیره عکس رو داره اول اجرا میشه و اکشن متد (Add) که وظیفه ذخیره اطلاعات رو داره دوم، ولی گاهی اوقات این ترتیب به هم میریزه و ابتدا اطلاعات ارسالی ذخیره میشه و بعد اکشن متد ذخیره عکس اجرا میشه.
سناریوی ما هم تا حدی شبیه به سناریویی هست که آقای احمدی مطرح کردند، ولی همونطور که گفتیم مشکل اصلی اینه که اکشن متدها هر بار با ترتیبهای متفاوت فراخوانی میشن
<div class="container-fluid"> @using (Ajax.BeginForm("Add", "Authors", new AjaxOptions { UpdateTargetId = "result", InsertionMode = InsertionMode.Replace, HttpMethod = "POST" }, new { @class = "form-horizontal", id = "UploadFile" })) { @Html.AntiForgeryToken() <div class="control-group"> <label class="control-label" for="AuhtorFirstNameAndLastName">نام نویسنده</label> <div class="controls"> @Html.TextBoxFor(author => author.AuhtorFirstNameAndLastName, new { placeholder = "نام نویسنده" }) </div> @Html.ValidationMessageFor(author => author.AuhtorFirstNameAndLastName) </div> <div class="control-group"> <label class="control-label" for="Status">ارسال عکس</label> <div class="controls"> <input type="file" name="avatarFile" id="avatarFile" /> </div> <div> @*<input type="submit" name="btn-submit" value="ارسال" class="btn btn-success" />*@ <img id="loading" alt="1" src="Images/loading83.gif" style="display: none;" /> </div> </div> <div id="result"></div> <input type="submit" name="btn-submit" value="افزودن نویسنده" class="btn btn-success" /> <input type="button" name="btn-colose" id="btn-close" value="انصراف" class="btn btn-danger" onclick="$dialog.dialog('close');" /> } </div> <script type="text/javascript"> $('#UploadFile').submit(function () { $("#loading").show(); $.ajaxFileUpload({ url: "@Url.Action("AddAvatar","Authors")", secureuri: false, fileElementId: 'avatarFile', dataType: 'json', data: {}, success: function (data, status) { $("#loading").hide(); }, error: function (data, status, e) { $("#loading").hide(); } }); }); </script>
من هم مشکل شمارو داشتم و کدهای فایل ajaxfileupload.js رو بررسی کردم و به این تکه کد برخورد کردم:
$("#" + formElementId + " > input[type='file']").each(function () { var oldElement = $(this); var newElement = jQuery(oldElement).clone(); jQuery(oldElement).attr("id", fileId); jQuery(oldElement).before(newElement); jQuery(oldElement).appendTo(form); });
کدهای سمت سرور دریافت فایل PDF
using Microsoft.AspNetCore.Mvc; namespace BlazorWasmShowBinaryFiles.Server.Controllers { [ApiController] [Route("api/[controller]")] public class ReportsController : ControllerBase { [HttpGet("[action]")] public IActionResult GetPdfReport() { //TODO: create the `sample.pdf` report file on the server return File(virtualPath: "~/app_data/sample.pdf", contentType: "application/pdf", fileDownloadName: "sample.pdf"); } } }
ساخت URL برای دسترسی به اطلاعات باینری
تمام مرورگرهای جدید از ایجاد URL برای اشیاء Blob دریافتی از سمت سرور، توسط متد توکار URL.createObjectURL پشتیبانی میکنند. این متد، شیء URL را از شیء window جاری دریافت میکند و سپس اطلاعات باینری را دریافت کرده و آدرسی را جهت دسترسی موقت به آن تولید میکند. حاصل آن، یک URL ویژهاست مانند blob:https://localhost:5001/03edcadf-89fd-48b9-8a4a-e9acf09afd67 که گشودن آن در مرورگر، یا سبب نمایش آن تصویر و یا دریافت مستقیم فایل خواهد شد.
در برنامههای Blazor نیاز است اینکار را توسط JS Interop آن انجام داد؛ از این جهت که API تولید یک Blob URL، صرفا توسط کدهای جاوا اسکریپتی قابل دسترسی است. به همین جهت فایل جدید Client\wwwroot\site.js را با محتوای زیر ایجاد کرده و همچنین مدخل آنرا در به انتهای فایل Client\wwwroot\index.html، پیش از بسته شدن تگ body، اضافه میکنیم:
window.JsBinaryFilesUtils = { createBlobUrl: function (byteArray, contentType) { // The byte array in .NET is encoded to base64 string when it passes to JavaScript. const numArray = atob(byteArray) .split("") .map((c) => c.charCodeAt(0)); const uint8Array = new Uint8Array(numArray); const blob = new Blob([uint8Array], { type: contentType }); return URL.createObjectURL(blob); }, downloadFromUrl: function (fileName, url) { const anchor = document.createElement("a"); anchor.style.display = "none"; anchor.href = url; anchor.download = fileName; document.body.appendChild(anchor); anchor.click(); document.body.removeChild(anchor); }, downloadBlazorByteArray: function (fileName, byteArray, contentType) { const blobUrl = this.createBlobUrl(byteArray, contentType); this.downloadFromUrl(fileName, blobUrl); URL.revokeObjectURL(blobUrl); }, printFromUrl: function (url) { const iframe = document.createElement("iframe"); iframe.style.display = "none"; iframe.src = url; document.body.appendChild(iframe); if (iframe.contentWindow) { iframe.contentWindow.print(); } }, printBlazorByteArray: function (byteArray, contentType) { const blobUrl = this.createBlobUrl(byteArray, contentType); this.printFromUrl(blobUrl); URL.revokeObjectURL(blobUrl); }, showUrlInNewTab: function (url) { window.open(url); }, showBlazorByteArrayInNewTab: function (byteArray, contentType) { const blobUrl = this.createBlobUrl(byteArray, contentType); this.showUrlInNewTab(blobUrl); URL.revokeObjectURL(blobUrl); }, };
- زمانیکه در برنامههای Blazor با استفاده از متد ()HttpClient.GetByteArrayAsync آرایهای از بایتهای یک فایل باینری را دریافت میکنیم، ارسال آن به کدهای جاوااسکریپتی به صورت یک رشتهی base64 شده صورت میگیرد (JS Interop اینکار را به صورت خودکار انجام میدهد). به همین جهت در متد createBlobUrl روش تبدیل این رشتهی base64 دریافتی را به آرایهای از بایتها، سپس به یک Blob و در آخر به یک Blob URL، مشاهده میکنید. این Blob Url اکنون آدرس موقتی دسترسی به آرایهای از بایتهای دریافتی توسط مرورگر است. به همین جهت میتوان از آن به عنوان src بسیاری از اشیاء HTML استفاده کرد.
- متد downloadFromUrl، کار دریافت یک Url و سپس دانلود خودکار آنرا انجام میدهد. اگر به یک anchor استاندارد HTML، ویژگی download را نیز اضافه کنیم، با کلیک بر روی آن، بجای گشوده شدن این Url، مرورگر آنرا دریافت خواهد کرد. متد downloadFromUrl کار ساخت لینک و تنظیم ویژگیهای آن و سپس کلیک بر روی آنرا به صورت خودکار انجام میدهد. از متد downloadFromUrl زمانی استفاده کنید که منبع مدنظر، محافظت شده نباشد و Url آن به سادگی در مرورگر قابل گشودن باشد.
- متد downloadBlazorByteArray همان کار متد downloadFromUrl را انجام میدهد؛ با این تفاوت که Url مورد نیاز توسط متد downloadFromUrl را از طریق یک Blob Url تامین میکند.
- متد printFromUrl که جهت دسترسی به منابع محافظت نشده طراحی شدهاست، Url یک منبع را دریافت کرده، آنرا به یک iframe اضافه میکند و سپس متد print را بر روی این iframe به صورت خودکار فراخوانی خواهد کرد تا سبب ظاهر شدن صفحهی پیشنمایش چاپ شود.
- printBlazorByteArray همان کار متد printFromUrl را انجام میدهد؛ با این تفاوت که Url مورد نیاز توسط متد printFromUrl را از طریق یک Blob Url تامین میکند.
تهیهی متدهایی الحاقی جهت کار سادهتر با JsBinaryFilesUtils
پس از تهیهی JsBinaryFilesUtils فوق، میتوان با استفاده از کلاس زیر که به همراه متدهایی الحاقی جهت دسترسی به امکانات آن است، کار با متدهای دریافت، نمایش و چاپ فایلهای باینری را سادهتر کرد و از تکرار کدها جلوگیری نمود:
using System.Threading.Tasks; using Microsoft.JSInterop; namespace BlazorWasmShowBinaryFiles.Client.Utils { public static class JsBinaryFilesUtils { public static ValueTask<string> CreateBlobUrlAsync( this IJSRuntime JSRuntime, byte[] byteArray, string contentType) { return JSRuntime.InvokeAsync<string>("JsBinaryFilesUtils.createBlobUrl", byteArray, contentType); } public static ValueTask DownloadFromUrlAsync(this IJSRuntime JSRuntime, string fileName, string url) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.downloadFromUrl", fileName, url); } public static ValueTask DownloadBlazorByteArrayAsync( this IJSRuntime JSRuntime, string fileName, byte[] byteArray, string contentType) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.downloadBlazorByteArray", fileName, byteArray, contentType); } public static ValueTask PrintFromUrlAsync(this IJSRuntime JSRuntime, string url) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.printFromUrl", url); } public static ValueTask PrintBlazorByteArrayAsync( this IJSRuntime JSRuntime, byte[] byteArray, string contentType) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.printBlazorByteArray", byteArray, contentType); } public static ValueTask ShowUrlInNewTabAsync(this IJSRuntime JSRuntime, string url) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.showUrlInNewTab", url); } public static ValueTask ShowBlazorByteArrayInNewTabAsync( this IJSRuntime JSRuntime, byte[] byteArray, string contentType) { return JSRuntime.InvokeVoidAsync("JsBinaryFilesUtils.showBlazorByteArrayInNewTab", byteArray, contentType); } } }
اصلاح Content Security Policy سمت سرور جهت ارائهی محتوای blob
پس از دریافت فایل PDF به صورت یک blob، با استفاده از متد URL.createObjectURL میتوان آدرس موقت محلی را برای دسترسی به آن تولید کرد و یک چنین آدرسهایی به صورت blob:http تولید میشوند. در این حالت در Content Security Policy سمت سرور، نیاز است امکان دسترسی به تصاویر و همچنین اشیاء از نوع blob را نیز آزاد معرفی کنید:
img-src 'self' data: blob: default-src 'self' blob: object-src 'self' blob:
نمایش فایل PDF دریافتی از سرور، به همراه دکمههای دریافت، چاپ و نمایش آن در صفحهی جاری
در ادامه کدهای کامل مرتبط با تصویری را که در ابتدای بحث مشاهده کردید، ملاحظه میکنید:
@page "/" @using BlazorWasmShowBinaryFiles.Client.Utils @inject IJSRuntime JSRuntime @inject HttpClient HttpClient <h1>Display PDF Files</h1> <button class="btn btn-info" @onclick="handlePrintPdf">Print PDF</button> <button class="btn btn-primary ml-2" @onclick="handleShowPdf">Show PDF</button> <button class="btn btn-success ml-2" @onclick="handleDownloadPdf">Download PDF</button> @if(!string.IsNullOrWhiteSpace(PdfBlobUrl)) { <section class="card mb-5 mt-3"> <div class="card-header"> <h4>using iframe</h4> </div> <div class="card-body"> <iframe title="PDF Report" width="100%" height="600" src="@PdfBlobUrl" type="@PdfContentType"></iframe> </div> </section> <section class="card mb-5"> <div class="card-header"> <h4>using object</h4> </div> <div class="card-body"> <object data="@PdfBlobUrl" aria-label="PDF Report" type="@PdfContentType" width="100%" height="100%"></object> </div> </section> <section class="card mb-5"> <div class="card-header"> <h4>using embed</h4> </div> <div class="card-body"> <embed aria-label="PDF Report" src="@PdfBlobUrl" type="@PdfContentType" width="100%" height="100%"> </div> </section> } @code { private const string ReportUrl = "/api/Reports/GetPdfReport"; private const string PdfContentType = "application/pdf"; private string PdfBlobUrl; private async Task handlePrintPdf() { // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization). // Otherwise just use the `PrintFromUrlAsync` method. var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl); await JSRuntime.PrintBlazorByteArrayAsync(byteArray, PdfContentType); } private async Task handleDownloadPdf() { // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization). // Otherwise just use the `DownloadFromUrlAsync` method. var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl); await JSRuntime.DownloadBlazorByteArrayAsync("report.pdf", byteArray, PdfContentType); } private async Task handleShowPdf() { // Note: Using the `HttpClient` is useful for accessing the protected API's by JWT's (non cookie-based authorization). // Otherwise just use the `ReportUrl` as the `src` of the `iframe` directly. var byteArray = await HttpClient.GetByteArrayAsync(ReportUrl); PdfBlobUrl = await JSRuntime.CreateBlobUrlAsync(byteArray, PdfContentType); } // Tips: // 1- How do I enable/disable the built-in pdf viewer of FireFox // https://support.mozilla.org/en-US/kb/disable-built-pdf-viewer-and-use-another-viewer // 2- How to configure browsers to use the Adobe PDF plug-in to open PDF files // https://helpx.adobe.com/acrobat/kb/pdf-browser-plugin-configuration.html // https://helpx.adobe.com/acrobat/using/display-pdf-in-browser.html // 3- Microsoft Edge is gaining new PDF reader features within the Windows 10 Fall Creator’s Update (version 1709). }
- پس از تهیهی JsBinaryFilesUtils و متدهای الحاقی متناظر با آن، اکنون تنها کافی است با استفاده از متد ()HttpClient.GetByteArrayAsync، فایل PDF ارائه شدهی توسط یک اکشن متد را به صورت آرایهای از بایتها دریافت و سپس به متدهای چاپ (PrintBlazorByteArrayAsync) و دریافت (DownloadBlazorByteArrayAsync) آن ارسال کنیم.
- در مورد نمایش آرایهای از بایتهای دریافتی، وضعیت کمی متفاوت است. ابتدا باید توسط متد CreateBlobUrlAsync، آدرس موقتی این آرایه را در مرورگر تولید کرد و سپس این آدرس را برای مثال به src یک iframe انتساب دهیم تا PDF را با استفاده از امکانات توکار مرورگر، نمایش دهد.
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: BlazorWasmShowBinaryFiles.zip