【前言】
任何內部系統與電商,都會使用「檔案上傳」都用 input type="File" 來處理檔案的上傳,現在的系統大部份不外乎也都是這麼做。 但是,以需求不同透過「檔案上傳」一次要上傳多個檔,那系統就是提供多個 input type="File" 來做到批次上傳的動作,還是一個 zip 裡面就包裹著多個檔案,上傳後再來一次解開 zip 來達到需求。方法很多,但是...都不是很視覺化,上傳檔案在現今應該可以更視覺化更親民才對。
但網路上所提供的都是一知半解,那就只好自己寫一支較方便的上傳工具做為往後自己的利器,並且套用在自己的系統開發上,並取代那個醜不拉機的系統介面。
【您必須要有的技術底層】
【應用技術與版型】
本人寫程式是比較往視覺化美觀設計,不想在自己的系統上看到一些醜不拉機管理介面,因為當您是使用者時,看到一些五顏六色操作畫面,說真的,我也不覺得程式的底層與邏輯可以寫的多少好。 尤其應用了一堆 EF 但卻連 TSQL 與 Stored Procedure 都不會寫,每次寫出來的 TSQL 卻只有一支程式可以用而己,說真的...貢獻度極低...又不寫文件的...說的多強那就自己去自嗨就好了...
[套版與上傳介面的部份]
檔案上傳版型,我會用套版的方式來進行整合,這次整合的版型是 Bootstrap 後台管理版型 Hyper >> Forms Dropzone File Upload
這個部份採用了 dropzone.min.js 所提供的 JS 程式來達到上傳的動作,Dropzone 所提供的 JS 也可以上傳整個目錄(您必須要用拖拉目錄的方式) 來進行上傳的動作,上傳後就會將圖片轉成Base64 ,但是他還沒有真正上傳到您的 Server 上喔!! 別以為這樣子就上傳完成了喔...
這動作您只能進行預覽的動作,後續您必須要自行撰寫接收程式。 那上傳後您覺得這顯示並不喜歡,那您可以修改 dropzone.JS >> previewTemplate: 這個部份的 HTML 就可以有不同的顯示方式。
[Highslide 小工具]
Highslide 大部份都是拿來做「預覽圖片」,Highslide 有支援 iframe 的方式,您可以將您上傳介面按鈕掛在 Highslide 提供的 ifram 顯示您的介面,例如以下方法...
<a href="您設計的 Dropzone 頁面"
class="btn btn-primary"
onclick="return hs.htmlExpand(this, {objectType:'iframe',width:'1024',height:'600',fixedControls:false})"><i class="mdi mdi-square-edit-outline"></i>選擇要上傳的檔案</a>
fixedControls:false 會以您「按下的位置」顯示 Highslide 介面,您可以找網路上的相關範例有其他的設定方式
[Dropzone 參數設定]
<script type='text/javascript'>
$(document).ready(function () {
Dropzone.autoDiscover = true;
//Simple Dropzonejs
$('#dZUpload').dropzone({
url:'「檔案的接收端,這裡我是用 xxxx.ashx」',
maxFiles:可以上傳多少個檔案 1~N 都可以,
maxFilesize:上傳一個檔案最高 MB 1=1MB , 10=10MB,
uploadMultiple:true,
addRemoveLinks:true,
acceptedFiles:'指定上傳的檔案格式 .jpg,.jpeg,.gif,.png,.mp4 ',
//以下是各項事件的說明
dictDefaultMessage: '將檔案拖放到這裡 (或這裡點擊)',
dictFallbackMessage: '您的瀏覽器不支援拖放檔案上傳',
dictFallbackText: '您的瀏覽器不支援拖放檔案上傳',
dictFileTooBig: '檔案大小限制:{{maxFilesize}}MB, 檔案太大 ({{filesize}}MB)',
dictInvalidFileType: '您可以上傳 jpg, jpeg, png 圖檔',
dictCancelUpload: '取消上傳',
dictCancelUploadConfirmation: '您確定取消上傳這張圖檔嗎?',
dictRemoveFile:'清除記錄',
dictRemoveFileConfirmation: '您確定刪除這張圖檔嗎?',
dictMaxFilesExceeded:'檔案個數限制:10',
success: function(file, response) {
var imgName = response;
file.previewElement.classList.add('dz-success');
console.log('Successfully uploaded :' + imgName);
},
error: function(file, response) {
//發生錯誤要執行的動作
file.previewElement.classList.add('dz-error');
},
init:function () {
//所有的檔案都己上傳完成
this.on('queuecomplete', function (file) {
//這裡您可以加上 ajax 事件
//您要自行取得您的 AJAX linkbutton 事件,
//例如像是 __doPostBack() 將 linkbutton onclick
//事件整個貼到 "這裡"
});
}
});
});
</script>
[Ajax 的設定]
所有的檔案都上傳完成,會觸發 init 程序。這時候您可以寫一段 Ajax 事件,那 Ajax 事件您要這樣子設計
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<span style="display: none;"> //用 span 將這控制項藏起來不顯示在畫面上
<asp:UpdatePanel ID=_UpdatePanel" runat="server">
<Triggers> //這部份一定要加 linkButton 項目
<asp:AsyncPostBackTrigger ControlID="_ReorganizePage" EventName="Click" />
</Triggers>
<ContentTemplate>
<%--功能事項--%>
<asp:LinkButton ID="_ReorganizePage" runat="server" Text="上傳完成觸發" OnClick="您的程序" />
</ContentTemplate>
</asp:UpdatePanel>
</span>
<asp:UpdatePanel ID="_Content_UpdatePanel_FilesUPLoad" runat="server">
<ContentTemplate>
將整個 HTML 全部包起來
<asp:Panel ID="_Panel01">
<DIV ID="dZUpload">
這整個 DIV 接收使用者拖曳過來的檔案或是目錄
</DIV>
</asp:Panel>
<asp:Panel ID="_Panel02">
當 Dropzone 上傳完成後一定會觸發 init,您要詢問使用者下一步要做什麼事情
例如上傳 10 張圖片後如果超過 10 張以上的圖片,那要使用者在操作介面上選擇
要選擇那一些圖片 「以符合程序」
我的做法是 >> 上傳完成 >> 穩藏 _Panel01 將 _Panel02 顯示出來,提供使用者確定
「現在程序您只能選擇 10 張圖片」超過的圖示在關閉 Highslide 視窗時或是再提供按
鈕來執行最後的程序,程式完成後就可以關閉 Highslide 的視窗
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
透過這樣子的設計,您就己經完成 「現在程序您只能選擇 10 張圖片」的需求,但要注意一件事情 >> 此 Dropzone 上傳介面是 "共用網頁程序" 所以在上傳時您必須要有 Temp 暫存的概念。
啟動上傳前,必須建立一個 Temp 目錄,我會用 GUID 來處理,例如 [A君] 12:00 要進行上傳,系統就要建立一個 Temp\20200912_6134CF5E357441AA8\ 而 [A君] 所上傳的檔案都會放在 "20200912_6134CF5E357441AA8" ,另一位 [B君] 12:01 也要透過介面進行檔案上傳動作,上傳前也是建立了一個目錄 "20200912_6111111111111" 來進行上傳的動作,這樣子檔案上傳時就不會 [A君] 在上傳而 [B君] 看到 [A君] 所上傳的檔案,那就是會很奇怪了...
另一個問題,建立目錄我會多加一個 [年月日] 最主要的目的,就是將過舊的目錄進行刪除的動作,當然您可以直接讀目錄建立的時間,然後再來刪除。看您自己如何處理過舊的目錄或檔案,不過真的記得要清掉不然數量一多,執行上會使得 Windows 跑很慢,那就會拖到執行效能了。 後續您如果要存到資料庫都可以,看您要如何做...
[ASP.NET ASHX 程式碼]
好了,最重要的接收源的方法如下,透過 Visual Studio 新增一個 [名稱自取].ASHX
using System;
using System.Web;
using System.IO;
using System.Drawing;
using System.Threading;
public class [您的 Class 名稱] : IHttpHandler
{
/// <summary>
/// 接收來源
/// </summary>
/// <param name="_HttpContext">接收資料源</param>
public void ProcessRequest(HttpContext _HttpContext)
{
//這裡的程式碼就要看您的能力了
// 1. 判斷檔案是否有重覆,建議重新取檔案名稱,取比較有意義的檔案名稱
// 2. 判斷目錄是否有建立
// 等等....其他的就看你如何做了
// 指定要導出的資料流內容
_HttpContext.Response.ContentType = "text/plain";
//開始接收檔案內容了
foreach (string s in _HttpContext.Request.Files)
{
//取得此檔案的內容
HttpPostedFile _HttpPostedFile = _HttpContext.Request.Files[s];
//將資料內容進行存檔的動作
_HttpPostedFile.SaveAs("目錄+檔案名稱,例如: C:\TEMP\20200912_6134CF5E357441AA8\aaa.???");
}
//回傳檔案的名稱
_HttpContext.Response.Write(_strWeb_InBoxFileName);
}
public bool IsReusable
{
get
{
return false;
}
}
}
【使用情境】
程序一:可以上傳 10 個檔案,每一個檔案最高上傳 MB 容量只能 10 MB ,只能上傳 .JPG,JPEG,.gif,.png 等檔案格式
程序二:只能上傳 1 個檔案,每一個檔案最高上傳 MB 容量只能 1 MB ,只能上傳 .png 檔案格式
修改 Dropzone 參數設定
程序一:
$('#dZUpload').dropzone({
maxFiles:10,
maxFilesize:10,
acceptedFiles:'.jpg,.jpeg,.gif,.png',
});
程序二:
$('#dZUpload').dropzone({
maxFiles:1,
maxFilesize:1,
acceptedFiles:'.png',
});
你可以用任何方式帶參數進去,我的習慣會直接用
第二點可以提供給其他系統使用共同的上傳介面,自行控制上傳檔案後要做什麼事情,然而 DropConfig 後面帶的參數我會建議加密或是用其他的方式,最好有加密後有過期的動作,以免後續網址被瀏覽器記錄,或是因為被外流而使得有心人來亂搞,當然您還有更多的動作來處理這樣子的問題
[DEMO]
https://drive.google.com/file/d/1Cu_1ZlraxklVrA4rq99KHk_T55O4uGVO/view?usp=sharing
【總結】
上傳方式百百種,那種是最好的可以自行考量。 就算是您設計與開發都是後端,那也是可以去吸收國外版型的風格,來多改善設計上的美觀,這也是一種學習方式也是一種進步。 現在的設計講求的是整合,看看別人所提供的資訊,並加以導入並吸取別人的經驗來轉化成自己的能力,對自己都是一個成長不是嗎??
有任何問題可以詢問,除了 Dropzone 相關的項目我無法回答之外,其他的都能夠針對主題...