Blazor 使用拖放(drag and drop)上傳檔案

AlexChow發表於2022-04-11

在很多上傳檔案的應用例項中, 都可以看到[拖放檔案到此上傳]這種騷功能 ,今天我們就來試試Blazor能不能完成這個想法.

原文連結:https://www.cnblogs.com/ysmc/p/16128206.html  

簡述HTML5拖放

拖放是HTML5標準的一部分,任何元素都能夠拖放,也能夠將本地的檔案拖放到頁面上。

設定元素可拖放

為了使元素可拖動,把 draggable 屬性設定為 true

<img draggable="true" />

拖放事件

有7個拖放事件可以捕獲,如下:

dragstart:開始拖元素觸發

dragenter:元素拖進可drop元素(繫結drop事件的元素)時觸發

dragover:當元素拖動到drop元素上時觸發

drop:當元素放下到drop元素觸發

dragleave :當元素離開drop元素時觸發

drag:每次元素被拖動時會觸發

dragend:放開拖動元素時觸發

完成一次拖放的事件過程是: dragstart –> dragenter –> dragover –> drop –> dragend

瀏覽器支援

Edge、Firefox、Opera 12、Chrome 以及 Safari 5 支援拖放。

拖拽上傳實現

1.新建工程n02drag,將專案新增到解決方案中

dotnet new blazorserver -o n02drag
dotnet sln add n02drag/n02drag.csproj

2.在資料夾wwwroot/lib,新增drag子資料夾,新建app.js資料夾

先阻止頁面預設的拖放行為,不然頁面會被拖放的檔案替換了。

//阻止瀏覽器預設行為
document.addEventListener( "dragleave", function(e) {
     e.preventDefault();
}, false);
document.addEventListener( "drop", function(e) {
     e.preventDefault();
}, false);
document.addEventListener( "dragenter", function(e) {
     e.preventDefault();
}, false);
document.addEventListener( "dragover", function(e) {
     e.preventDefault();
}, false);

設定drop區域

當檔案拖放到drop區域時,就能觸發上傳。

    element.addEventListener("drop", function (e) {

        try {
            var fileList = e.dataTransfer.files; //獲取檔案物件
            //檢測是否是拖拽檔案到頁面的操作
            if (fileList.length == 0) {
                return false;
            }

            inputFile.files = e.dataTransfer.files;
            const event = new Event('change', { bubbles: true });
            inputFile.dispatchEvent(event);
        }
        catch (e) {
            wrapper.invokeMethodAsync('DropAlert', e);
        }
    }, false);

2.在檔案Pages/Index.razor,新增上傳元件

頁面

@implements IAsyncDisposable
@inject IJSRuntime JS

<div @ref="UploadElement" style="padding: 20px; width: 200px; height: 200px; background-color: cornflowerblue; border: 2px dashed #0087F7; border-radius: 5px; ">
    <p>拖放上傳檔案</p>
     <InputFile OnChange="OnChange" class="form-control" multiple @ref="inputFile"/>
</div>

<pre>
<code>
        @uploadstatus
</code>
</pre>

程式碼

  1. InputFile 開啟 multiple ,接受多檔案上傳
  2. 使用JS隔離技術
  3. Dispose處理回收

@code{
    [Inject] protected Microsoft.AspNetCore.Hosting.IWebHostEnvironment? HostEnvironment { get; set; } //獲取IWebHostEnvironment

    protected ElementReference UploadElement { get; set; }
    protected InputFile? inputFile { get; set; }

    private DotNetObjectReference<Index>? wrapper;

    private IJSObjectReference? module;
    private IJSObjectReference? dropInstance;

    protected string UploadPath = "";
    protected string? uploadstatus;
    long maxFileSize = 1024 * 1024 * 15;

    protected override void OnAfterRender(bool firstRender)
    {
        if (!firstRender) return;
        UploadPath = Path.Combine(HostEnvironment!.WebRootPath, "Upload"); //初始化上傳路徑
        if (!Directory.Exists(UploadPath)) Directory.CreateDirectory(UploadPath); //不存在則新建目錄
    }

    protected async Task OnChange(InputFileChangeEventArgs e)
    {
        int i = 0;
        var selectedFiles = e.GetMultipleFiles(100);
        foreach (var item in selectedFiles)
        {
            i++;
            await OnSubmit(item);
            uploadstatus += Environment.NewLine + $"[{i}]: " + item.Name;
        }
    }

    protected async Task OnSubmit(IBrowserFile efile)
    {
        if (efile == null) return;
        var tempfilename = Path.Combine(UploadPath, efile.Name);
        await using FileStream fs = new(tempfilename, FileMode.Create);
        using var stream = efile.OpenReadStream(maxFileSize);
        await stream.CopyToAsync(fs);
        StateHasChanged();
    }


    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender) return;

        module = await JS.InvokeAsync<IJSObjectReference>("import", "./lib/drag/app.js");
        wrapper = DotNetObjectReference.Create(this);
        dropInstance = await module.InvokeAsync<IJSObjectReference>("init", wrapper , UploadElement, inputFile!.Element);
    }

    [JSInvokable]
    public void DropAlert(string msg)
    {
        uploadstatus  += Environment.NewLine + $"[!Alert!]: " + msg;
        StateHasChanged();
    }


    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (dropInstance != null)
        {
            await dropInstance.InvokeVoidAsync("dispose");
            await dropInstance.DisposeAsync();
        }

        if (wrapper != null)
        {
            wrapper.Dispose();
        }

        if (module != null)
        {
            await module.DisposeAsync();
        }
    }

}

3.就這麼簡單嗎?我們還可以加上一些騷功能 ?? 貼上檔案上傳

    element.addEventListener('paste', function (e) {
    
        inputFile.files = e.clipboardData.files;
        const event = new Event('change', { bubbles: true });
        inputFile.dispatchEvent(event);
    }, false);

4.限制傳輸格式

*時間有限,js部分同學們自己完成吧,這裡是InputFile 上限制一下

<div style="padding-top:40px; padding: 20px; width: 200px; height: 200px; background-color: cornflowerblue; border: 2px dashed #0087F7; border-radius: 5px; ">
    上傳圖片
    <InputFile OnChange="OnChange" style="max-width: 400px" class="form-control" multiple accept='image/*' />
</div>

5.完整JS程式碼

export function init(wrapper, element, inputFile) {

    //阻止瀏覽器預設行為
    document.addEventListener("dragleave", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("drop", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("dragenter", function (e) {
        e.preventDefault();
    }, false);
    document.addEventListener("dragover", function (e) {
        e.preventDefault();
    }, false); 

    element.addEventListener("drop", function (e) {

        try {
            var fileList = e.dataTransfer.files; //獲取檔案物件
            //檢測是否是拖拽檔案到頁面的操作
            if (fileList.length == 0) {
                return false;
            }

            inputFile.files = e.dataTransfer.files;
            const event = new Event('change', { bubbles: true });
            inputFile.dispatchEvent(event);
        }
        catch (e) {
            wrapper.invokeMethodAsync('DropAlert', e);
        }
    }, false);

    element.addEventListener('paste', function (e) {
    
        inputFile.files = e.clipboardData.files;
        const event = new Event('change', { bubbles: true });
        inputFile.dispatchEvent(event);
    }, false);

    return {
        dispose: () => {
            element.removeEventListener('dragleave', onDragLeave);
            element.removeEventListener("drop", onDrop);
            element.removeEventListener('dragenter', onDragHover);
            element.removeEventListener('dragover', onDragHover);
            element.removeEventListener('paste', handler);
        }
    }
}

拖放

參考資料

用20行程式碼實現檔案上傳,瀏覽目錄功能 (Blazor server) https://github.com/densen2014/Blazor100/wiki/9.-用20行程式碼實現檔案上傳,瀏覽目錄功能-(Blazor-server)

HTML5拖放(drag and drop)與plupload的懶人上傳 https://www.programminghunter.com/article/31061232701/

專案原始碼

Github | Gitee

知識共享許可協議

本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名AlexChow(包含連結: https://github.com/densen2014 ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡

相關文章