如题
按官方文档实现,下载过程无进度,不知道园子里有没有大神知道怎么解决这个问题。
https://learn.microsoft.com/zh-cn/aspnet/core/blazor/file-downloads?view=aspnetcore-7.0
我知道用原生的asp.net core api/mvc都可以实现,但不想在Blazor Service项目中嵌入api/mvc/Razor Pages
Blazor Service模式下的文件下载没有进度的问题可以通过以下方式解决:
以下是使用HttpClient的Progress属性实现文件下载进度的示例代码:
在服务端代码中,定义一个下载文件的方法,该方法将HttpClient对象作为参数传递给服务端方法:
using System.Net.Http;
using System.IO;
using System.Threading.Tasks;
public async Task DownloadFile(HttpClient httpClient)
{
var response = await httpClient.GetAsync("http://example.com/file.pdf", HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var contentLength = response.Content.Headers.ContentLength;
var stream = await response.Content.ReadAsStreamAsync();
using var fileStream = new FileStream("file.pdf", FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
byte[] buffer = new byte[8192];
var totalRead = 0L;
int read;
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileStream.WriteAsync(buffer, 0, read);
totalRead += read;
var progress = (double)totalRead / contentLength;
// 将下载进度发送给客户端
}
}
在客户端代码中,调用服务端方法,并使用HttpClient的Progress属性来获取下载进度信息:
@using System.Net.Http
<button @onclick="DownloadFile">Download File</button>
@code {
private readonly HttpClient _httpClient = new HttpClient();
private async Task DownloadFile()
{
await MyService.DownloadFile(_httpClient);
}
private void OnProgress(HttpContentProgressEventArgs args)
{
// 更新下载进度
}
private void OnCompleted(HttpResponseMessage response)
{
// 下载完成
}
}
在Blazor组件中注入服务端方法:
@using Microsoft.AspNetCore.Components
@inject MyService MyService
以上示例代码中,需要将下载进度发送给客户端,可以使用SignalR或者Blazor的JavaScriptInterop来实现。
@LuoCore: 谢谢,不过好像不对。
你这个DownloadFile 方法是在服务端运行的吧?那 var fileStream = new FileStream("file.pdf", FileMode.Create, FileAccess.Write, FileShare.None, 4096, true); 就是在服务端创建保存文件吧?
文件下载的目的是把服务器上的文件(磁盘上的静态文件和实时生成的虚拟文件流/字节)保存到客户端本地电脑磁盘。
其实服务器上有静态文件还好办,后端配置app.UseStaticFiles 或直接放置到wwwroot目录,用js直接打开文件路径(url)浏览器就能自动下载了,下载时有进度。
而且官方文件也有相应的解决方案(第二种方案,URL下载)。
https://learn.microsoft.com/zh-cn/aspnet/core/blazor/file-downloads?view=aspnetcore-7.0
现在的问题是,服务器上没有落盘,无具体实体文件(文件存储在对象存储、数据库或即时生成)。
因为没有实体文件,所以就没有供客户端访问的地址。
上面的官方文件第一种方案就是这种,但它是直接将二进制流转成JS,通过Blob、URL.createObjectURL()在客户端机器上生成虚拟URL来下载,可以下载,但下载过程无进度。
已经解决了,问题的关键在于不落盘的文件没有供客户端访问的URL地址,那么我们可以给它生成一个地址,用asp.net core api/mvc/razor page都可以实现,但这些太重了点。所有最后采用中件间来完成文件下载。
这样就可以摆脱Blazor Service单页面的限制,通过传统方法(URL)下载了。
Blazor Server模式是基于SignalR实现的,可以使用SignalR实时更新下载进度。但是Blazor WebAssembly模式是在客户端运行的,无法直接与服务器交互以获取下载进度。
为了解决这个问题,你可以通过在客户端实现一个进度条来模拟下载进度。这个进度条可以显示下载进度的百分比,并随着时间的推移逐渐增加。
你可以尝试使用JavaScript的XMLHttpRequest对象来实现进度条。XMLHttpRequest对象可以用于向服务器发送异步请求,并在请求过程中报告进度。在请求期间,XMLHttpRequest对象将触发progress事件,该事件将提供有关请求进度的信息,例如下载的字节数和总字节数。
以下是一个示例,演示如何使用XMLHttpRequest对象在Blazor WebAssembly应用程序中实现下载进度:
在你的Blazor WebAssembly项目中创建一个名为DownloadService的服务类。
在DownloadService类中添加一个DownloadFileAsync方法,用于下载文件。这个方法应该返回一个Task<byte[]>对象,表示下载的文件内容。
在DownloadFileAsync方法中,使用XMLHttpRequest对象从服务器下载文件,并在下载过程中报告进度。
在Blazor WebAssembly应用程序中创建一个DownloadButton组件,并将DownloadService注入到该组件中。
当用户单击DownloadButton时,调用DownloadService的DownloadFileAsync方法,获取下载的文件内容,并使用JavaScript代码将下载进度显示在进度条中。
有点晕,用 XMLHttpRequest 和前后端分离项目中在前端用JS调用api/mvc URL地址下载差不多吧?
但是如何在 Blazor 中用JS、XMLHttpRequest 调用 DownloadService的DownloadFileAsync
@Adming: 在Blazor应用程序中,可以使用JSInterop来调用JavaScript代码。JSInterop允许您在C#代码中调用JavaScript函数,从而使您能够使用XMLHttpRequest对象从服务器下载文件。您可以在以下几个步骤中完成此操作:
创建一个JavaScript函数,该函数使用XMLHttpRequest对象从服务器下载文件。您可以使用XMLHttpRequest对象来下载文件并将其存储在本地计算机上的任何位置。
例如,以下JavaScript函数可以使用XMLHttpRequest对象从服务器下载文件:
function downloadFile(url, filename) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function() {
var a = document.createElement('a');
a.href = window.URL.createObjectURL(xhr.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
a.remove();
};
xhr.send();
}
这个JavaScript函数将在XMLHttpRequest对象的onload事件触发时下载文件。它创建一个HTML链接元素,将链接元素的href属性设置为一个Blob URL,然后使用download属性指定文件名。最后,它将链接元素添加到文档中,触发单击事件来下载文件,然后将链接元素从文档中删除。
在Blazor应用程序中创建一个JavaScriptInterop类,该类使用JSInterop来调用JavaScript函数。您可以将此类注入到您的组件中,以便您可以在C#代码中使用它。
例如,以下JavaScriptInterop类将调用上述JavaScript函数:
public class DownloadService
{
private readonly IJSRuntime jsRuntime;
public DownloadService(IJSRuntime jsRuntime)
{
this.jsRuntime = jsRuntime;
}
public async Task DownloadFileAsync(string url, string filename)
{
await jsRuntime.InvokeVoidAsync("downloadFile", url, filename);
}
}
在Blazor应用程序中使用DownloadService类来下载文件。您可以在组件中注入DownloadService类,并使用它来从服务器下载文件。
例如,以下代码演示了如何在Blazor组件中使用DownloadService类:
@page "/download"
@inject DownloadService DownloadService
<button @onclick="DownloadFile">Download File</button>
@code {
private async Task DownloadFile()
{
await DownloadService.DownloadFileAsync("/api/download/myfile.pdf", "myfile.pdf");
}
}
这个组件在点击按钮时将调用DownloadFile方法。DownloadFile方法将使用注入的DownloadService对象来调用DownloadFileAsync方法,并传递URL和文件名参数。下载将在浏览器中自动开始,并将文件保存到本地计算机上的默认下载目录中。
请注意,您需要将XMLHttpRequest对象与您的Web服务器交互,以便下载文件。在Blazor应用程序中,您可以使用Blazor WebAssembly或Blazor Server模型。如果您使用Blazor WebAssembly模型,XMLHttpRequest对象将直接与您的Web API交互。如果您使用Blazor Server模型,则需要将XMLHttpRequest对象发送到Web服务器,然后将文件下载到客户端
@Technologyforgood: /api/download/myfile.pdf 这个地址从那儿来
你这个实现的前提是在服务器上有这个静态文件吧。
如果服务器上有文件,就不用这么麻烦,直接用Js库 Axios、 jquery ajax 甚至直接浏览器输入这个地址就可以下载来,走http通道。
我现在的问题是,在Blazor这种单页面应用中没有办法暴露一个独立的URL地址出来供客户端下载。
自已解决了