程式範例 - 以全螢幕瀏覽器視窗檢視 PDF
3 |
事發總有原因,這段程式與 IE 轉 Edge/Chrome 有關。
上回提到因為 Edge/Chrome 都支援用瀏覽器顯示 PDF,若在 window.open() 新開視窗用 <a target="_blank"> 開啟 PDF 連結,PDF 會開在底下瀏覽器的新頁籤,導致操作動線混亂。我想到在 <a> 加註 download 的解法,讓強迫瀏覽器下載檔案,用 Acrobat Reader 之類軟體開啟可避開問題。不過很快接到通報,若使用者將 Edge/Chrome 設成預設開啟 PDF 的應用程式,便會出現「下載 PDF 檔存到本機,然後 PDF 開在底層 Edge 的新頁籤」的可笑結果,靠北! 我繞一大圈是為了回到原點...
另一方面,也有人反映,既然瀏覽器能直接開啟 PDF,強迫下載到本機再開啟是開倒車。上回有讀者也提到這點,只看一次的檔案沒必要特別儲存,將來還要花工夫刪除,不無道理。
幾經琢磨,從善如流,微調前端後端設法解決問題。
第一個要處理的是上回的老問題:如何避免 Edge/Chrome 在新頁籤開啟 PDF?簡單有效的做法是改用 window.open 加上 popup=yes 參數,為防止新開視窗被其他視窗覆寫,我想到一次開滿全螢幕的做法,確保使用者一定會看到,也不會因點其他程式被覆蓋。開滿全螢幕可透過 window.open() 的 top、left、width、height 等參數實現。其中有個眉角:在多螢幕環境,目前所在螢幕的左上角不一定是 0, 0!
以下圖為例,1 號螢幕解析度為 1280x720,因擔任主螢幕左上角 top 與 left 均為 0;2 號螢幕緊接 1 號螢幕右側,故 left 為 1280,其頂端高於 1 號螢幕,top 為 -308:
要查詢所在螢幕左上角座標有兩個非標準化的 API screen.availTop 及 screen.availLeft 可用,雖然非標準 API,但 PC 環境的 Chrome/Edge 都支援;螢幕寬度及高度可由 screen.availWidth 及 screen.availHeight 取得。(另有 screen.width 及 screen.heght 為螢幕完整尺寸,但可能包含無法用到的工作列區域,用 availWidth/availHeight 比較準)
以下示範例以全螢幕開啟連結的三種寫法,分別使用 jQuery、古老的 onclick="" 事件,以及 addEventListener() 將 <a href="some.pdf" > 改為全螢幕新視窗開啟。三種做法都有加上 return false 或 e.preventDefault(),目的在取消原本點擊 <a> 的開啟連結動作,不然原網頁會被換掉。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PDF 檢視</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<ul>
<li><a href="test.pdf" class="fullwin">開啟 PDF</a> (jQuery)</li>
<li><a href="test.pdf" onclick="openInFullScreenWin(this.href);return false">開啟 PDF</a>
(onclick)</li>
<li><a href="test.pdf" id="pdfLink">開啟 PDF</a> (addEventListener)</li>
</ul>
<script>
$(document).on('click', '.fullwin', function() {
openInFullScreenWin(this.href);
return false;
});
document.getElementById('pdfLink').addEventListener('click', function(e) {
openInFullScreenWin(e.target.href);
e.preventDefault();
});
function openInFullScreenWin(url) {
window.open(url, '_blank', 'popup=yes,top=' + screen.availTop +
',left=' + screen.availLeft +
',width=' + screen.availWidth +
',height=' + screen.availHeight);
}
</script>
</body>
</html>
前端處理完,若連結是靜態 PDF 檔,到這裡就結束了。若 PDF 內容由 ASP.NET 動態傳回,則還有一些注意事項。動態傳回 PDF 內容的 ASP.NET 程式(例如:download-pdf.aspx)通常會像下面這樣設定 ContentType 為 application/octet-stream 並加上 Content-Disposition: attachment; filename="檔案名(記得要編碼)"
指定下載檔名。
<%@Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "appliation/octet-stream";
Response.AddHeader("Content-Disposition",
"attachment; filename=\""" + Uri.EscapeDataString("test.pdf") + "\"");
Response.BinaryWrite(SimuReadPdfContent());
Response.End();
}
byte[] SimuReadPdfContent()
{
//...傳回 PDF 內容
}
</script>
這種寫法會觸發瀏覽器執行下載動作,如想在瀏覽器直接開啟,ContentType 要改成 application/pdf,並移除 Content-Disposition: attachment。
<%@Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "appliation/pdf";
Response.BinaryWrite(SimuReadPdfContent());
Response.End();
}
byte[] SimuReadPdfContent()
{
//...傳回 PDF 內容
}
</script>
這樣子 PDF 即可直接用瀏覽器檢視,但有個小問題是另存檔案時,預設檔名為 ASPX 名稱:
克服這個問題,還是要加 Content-Dispoistion 但 attachement 改成 inline:
void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "appliation/octet-stream";
Response.AddHeader("Content-Disposition",
"inline; filename=\""" + Uri.EscapeDataString("test.pdf") + "\"");
Response.BinaryWrite(SimuReadPdfContent());
Response.End();
}
最終成果 - 以全螢幕瀏覽器視窗顯示 PDF 文件,並可指定另存檔名。
Tipe of window.open() a url in fullscreen size to prevent from overlayed by other windows.
Comments
# by SL
用 iframe + request full screen 不行嗎?
# by Jeffrey
to SL, 又學到新東西了,requestFullscreen 應該也是種解法,感謝分享。
# by 小黑
nice,讚嘆黑大