我想拥有一个SPA,可以完成客户端的所有工作,甚至生成一些图形/视觉效果。
我希望能够使用户单击按钮并保存页面中的视觉效果,表格和其他内容(可见和不可见,因此右键单击保存或复制/粘贴并不总是选项)。
如何从webassembly / blazor库中调用函数,获取其结果并将其另存为客户端文件?
这个主意是这样的...?
cshtml
<input type="file" onchange="@ReadFile">
<input type="file" onchange="@SaveFile">
@functions{
object blazorObject = new blazorLibrary.SomeObject();
void ReadFile(){
blazorObject.someFunction(...selectedFile?...);
}
void SaveFile(){
saveFile(...selectedFile..?)
}
}
答案 0 :(得分:6)
Blazor的创建者史蒂夫·桑德森(Steve Sanderson)在上一次演讲中曾使用JavaScript互操作完成类似的任务。
上找到示例解决方案包括三个部分:
1)JavaScript
function saveAsFile(filename, bytesBase64) {
var link = document.createElement('a');
link.download = filename;
link.href = "data:application/octet-stream;base64," + bytesBase64;
document.body.appendChild(link); // Needed for Firefox
link.click();
document.body.removeChild(link);
}
2)C#互操作包装器
public static class FileUtil
{
public static Task SaveAs(string filename, byte[] data)
=> JSRuntime.Current.InvokeAsync<object>(
"saveAsFile",
filename,
Convert.ToBase64String(data));
}
3)从您的组件调用
FileUtil.SaveAs("Filename.dat", new byte[] {});
您可以在Blazor Fiddle
中看到它的一个动作答案 1 :(得分:5)
<a class="form-control btn btn-primary" href="/download?name=test.txt" target="_blank">Download</a>
@page "/download"
@model MyNamespace.DownloadModel
public class DownloadModel : PageModel
{
public IActionResult OnGet(string name) {
// do your magic here
var content = new byte[] { 1, 2, 3 };
return File(content, "application/octet-stream", name);
}
}
答案 2 :(得分:5)
由于我的声誉低下,我现在无法评论Eugene Shmakov的帖子,我只想添加一些内容。也许对于大多数人来说,这是显而易见的,但对我而言,不是。您必须像这样将saveAsFile.js添加到index.html文件中:
<script src="saveAsFile.js"></script>
花点时间找出答案。
答案 3 :(得分:4)
我创建了一个存储库和nuget软件包来解决和简化此问题,请看一下:https://github.com/arivera12/BlazorDownloadFile
答案 4 :(得分:1)
Eugene提出的解决方案有效,但是有一个缺点。如果尝试使用大文件,浏览器将在将Blob下载到客户端时挂起。我发现的解决方案是稍微更改一下代码并将文件存储在临时目录中,然后让服务器使用其机制来提供文件,而不是将其作为Blob推送。
在服务器配置中添加:
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(___someTempDirectoryLocation___, "downloads")),
RequestPath = "/downloads"
});
这会将静态链接添加到系统上某个位置的下载文件夹。将您要下载的所有文件放在其中。
下一步,您可以使用 http:// pathToYourApplication / downloads / yourFileName 使用指向该文件的链接,也可以使用主要示例中的简化保存javascript:
function saveAsFile(filename) {
var link = document.createElement('a');
link.download = filename;
link.href = "/downloads/" + filename;
document.body.appendChild(link); // Needed for Firefox
link.click();
document.body.removeChild(link);
}
它将为您推送到用户浏览器。
答案 5 :(得分:0)
byte作为base64字符串与二进制文件不同。
答案 6 :(得分:0)
答案 7 :(得分:0)
我是这样做的:
在名为 Controllers 的文件夹中添加了一个新的 DownloadController.cs
[Controller, Microsoft.AspNetCore.Mvc.Route("/[controller]/[action]")]
public class DownloadController : Controller
{
private readonly IDataCombinerService DataCombinerService;
private readonly IDataLocatorService DataLocatorService;
public DownloadController(IDataCombinerService dataCombinerService, IDataLocatorService dataLocatorService)
{
DataCombinerService = dataCombinerService;
DataLocatorService = dataLocatorService;
}
[HttpGet]
[ActionName("Accounts")]
public async Task<IActionResult> Accounts()
{
var cts = new CancellationTokenSource();
var Accounts = await DataCombinerService.CombineAccounts(await DataLocatorService.GetDataLocationsAsync(cts.Token), cts.Token);
var json = JsonSerializer.SerializeToUtf8Bytes(Accounts, Accounts.GetType(), new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true });
var stream = new MemoryStream(json);
var fResult = new FileStreamResult(stream, MediaTypeNames.Application.Json)
{
FileDownloadName = $"Account Export {DateTime.Now.ToString("yyyyMMdd")}.json"
};
return fResult;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
}
严格来说这里不需要 async,因为它不需要处理任何其他事情,但该方法用于在屏幕上显示相同的结果时。
然后在 Startup.cs 中
app.UseEndpoints(endpoints =>
添加:
endpoints.MapControllerRoute(
name: "default",
defaults: new { action = "Index" },
pattern: "{controller}/{action}");
endpoints.MapControllers();
同样,严格来说默认值不是必需的,它是一个标准的 MVC 控制器。
这就像经典的 MVC 响应一样,因此您可以从您喜欢的任何来源发回任何文件。使用中间件服务在视图和下载器控制器之间保存临时数据可能会有所帮助,以便客户端下载相同的数据。