我找到了BlazorInputFile库,但从2019年10月起仍有打开的PR,我不确定这个库是否仍在维护。此外,我在博客中发现了几篇关于如何在Blazor中使用JS上传文件的文章。如果可能的话,我不想使用JS,但我确实需要使用Blazor上传文件。。。在不使用JavaScript的情况下可以做到这一点吗?
我曾尝试安装StevenSandersonMS的repo,然后意识到,截至2021年2月,ASP.NET Core 5.0中实际上有一个本机InputFile
组件。
它支持在Blazor中上传单个和多个文件,并且易于使用(并且您不需要添加自己的JS文件等(。
我用它来上传单个文件——你所需要做的就是在Razor页面中添加InputFile
组件:
<InputFile OnChange="@SingleUpload" />
然后在我的情况下,我需要字节数组中的文件:
@code {
private async Task SingleUpload(InputFileChangeEventArgs e)
{
MemoryStream ms = new MemoryStream();
await e.File.OpenReadStream().CopyToAsync(ms);
var bytes = ms.ToArray();
//do something with bytes
}
}
InputFileChangeEventArgs
为您提供了IBrowserFile
的IReadOnlyList
,您可以使用它来获得Name
、LastModified
、Size
和ContentType
,以及OpenReadStream
获得Stream
的方法。
关于如何在ASP.NET文档中获取多个文件,有很好的文档和代码。
您还需要添加System.IO
名称空间:
@using System.IO
截至2020年6月,假设您正在使用表单,最好的方法(WA(是使用Tewr的FileReader。让我们从API开始,后控制器将是:
public async Task<IActionResult> PostMedia(
[FromForm] IFormFile Picture,
[FromForm] string Focus,
[FromForm] string ID,
[FromForm] string Title,
[FromForm] string FormType,
[FromForm] string AnimalType,
[FromForm] string Mode,
[FromForm] string AnimalID
)
{
Debug.WriteLine($"-------------------------------------{Focus}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{ID}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{Title}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{FormType}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{AnimalType}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{Mode}-----------------------------------------------");
Debug.WriteLine($"-------------------------------------{AnimalID}-----------------------------------------------");
//check if file was fully uploaded
if (Picture.Length == 0 || Picture == null)
return BadRequest("Upload a new File");
else
return Ok ("do something with this data....")
}
然后客户端的post方法是:
public async Task PostFile()
{
//create content headers
var content = new MultipartFormDataContent();
content.Headers.ContentDisposition = new
System.Net.Http.Headers.ContentDispositionHeaderValue("form-data");
//create content
content.Add(new StreamContent(Pic.Stream, (int)Pic.Stream.Length), "Picture", Pic.FileName);
content.Add(new StringContent(Pic.Title), "Title");
content.Add(new StringContent(Pic.Focus), "Focus");
content.Add(new StringContent(Pic.ID), "ID");
content.Add(new StringContent(Pic.FormType), "FormType");
content.Add(new StringContent(Pic.AnimalType), "AnimalType");
content.Add(new StringContent(Pic.Mode), "Mode");
content.Add(new StringContent(Pic.AnimalID), "AnimalID");
//call to the server
var upload = await Http.PostAsync("Media",content);
//get server response
Pic.Message = await upload.Content.ReadAsStringAsync();
}
Tewr文件读取器可以帮助您将文件读取到流中,在我的情况下,流会传递给Pic对象。与表单中输入元素的onchange绑定的读取函数是:
public async Task ReadFile()
{
var file = (await fileReaderService.CreateReference(Xelement).EnumerateFilesAsync()).FirstOrDefault();
if (file == null) return;
var fileInfo = await file.ReadFileInfoAsync();
Pic.FileName = fileInfo.Name;
// Read into RAM
using (var memoryStream = await file.CreateMemoryStreamAsync((int)fileInfo.Size))
{
// Copy store image into pic object
Pic.Stream = new MemoryStream(memoryStream.ToArray());
}
}
请注意,Xelement是ElementReference,它在表单.中用作输入元素的ref
在目前的情况下(截至2020年4月2日(,您将需要JS,这是不可避免的。
你可以采取两种主要方法:
-
在输入的
onchange
事件中获取文件数据,并通过将byte[]
传递给C#方法来调用它们-这基本上是您链接的文件选择器方法,您可以在Blazor应用程序中获得文件数据,用它做任何您想做的事。 -
在输入的
onchange
事件中获取文件数据,并使用JS调用一个远程端点,该端点将接收该文件并对其进行处理(例如将其保存在NAS上或放入DB中(。这是一个实际的文件上传,而不是文件选择器。
从编码的角度来看,这两种方法是相似的——您需要JS。也许在Blazor的未来版本中,我们将获得一个<InputFile>
,它将进行选择,这样您就可以使用C#HTTP请求进行上传。
文件选择器方法相对容易实现(实际上只有几行(,但它不会在服务器上为您提供文件,您必须为它做一些工作。"文件上传"方法更难做到正确。我个人会用别人的包裹。对于文件上传,像Blazor的Telerik UI这样的东西可以在商业上使用,对于更简单的选择器,已经有了另一个链接示例的答案。顺便说一句,Telerik的演示还有一个这样的例子,即为一些演示实现的组件。
我通过使用一个组件和一些javascript(看起来像一个按钮(来实现这一点。一旦组件和js被合并,你就再也不用担心了。。。
这是上传组件(Upload.Razar(:
@inject IJSRuntime JSRuntime
@if (AllowMulitple)
{
<input id="Xinputfile00" type="file" accept="@Filter" @onchange="UploadFile" multiple hidden />
}
else
{
<input id="Xinputfile00" type="file" accept="@Filter" @onchange="UploadFile" hidden />
}
<button class="btn btn-default" @onclick="ClickUpload">@Title</button>
@code {
[Parameter]
public FileData[] Files { get; set; }
[Parameter]
public string Filter { get; set; }
[Parameter]
public string Title { get; set; }
[Parameter]
public bool AllowMulitple { get; set; }
[Parameter]
public Action Uploaded { get; set; }
async Task UploadFile()
{
string[] result = await JSRuntime.InvokeAsync<string[]>("blazorExtensions.GetFileData", "Xinputfile00");
List<FileData> results = new List<FileData>();
foreach (string file in result)
{
results.Add(new FileData(file));
}
this.Files = results.ToArray();
if (Uploaded != null)
{
Uploaded();
}
}
async Task ClickUpload()
{
await JSRuntime.InvokeVoidAsync("blazorExtensions.InvokeClick", "Xinputfile00");
}
public class FileData
{
public string Base64 { get; set; }
public string MIMEType { get; set; }
public byte[] Bytes
{
get
{
return Convert.FromBase64String(this.Base64);
}
}
public FileData(string data)
{
if (string.IsNullOrWhiteSpace(data) || !data.Contains(","))
{
return;
}
string[] alldata = data.Split(',');
this.MIMEType = alldata[0].Remove(0, 5).Replace(";base64", "");
this.Base64 = alldata[1];
}
}
以下是javascript摘录:
window.blazorExtensions = {
GetFileData: async function (id) {
var target = document.getElementById(id);
var filesArray = Array.prototype.slice.call(target.files);
return Promise.all(filesArray.map(window.blazorExtensions.fileToDataURL));
},
fileToDataURL: async function (file) {
var reader = new FileReader();
return new Promise(function (resolve, reject) {
reader.onerror = function () {
reader.abort();
reject(new DOMException('Error occurred reading file ' + file));
};
reader.onload = function (event) {
resolve(reader.result);
console.log('resolved');
};
reader.readAsDataURL(file);
console.log('returned');
})
},
InvokeClick: function (id) {
var elem = document.getElementById(id);
if (typeof elem.onclick == "function") {
elem.onclick.apply(elem);
}
elem.click();
},
}
这里有一个调用标记示例:
<Upload @ref="upload" Filter=".xlsx" Title="Upload" AllowMulitple="false" Uploaded="DoMyExcelThingOrSomething" />
以及上传后调用的方法:
Upload upload;
void DoMyExcelThingOrSomething()
{
if (upload.Files.Length < 1 || string.IsNullOrWhiteSpace(upload.Files[0].Base64))
{
//...nothing good here...
return;
}
//play with upload.Files here...
}
对于Blazor Server,下面将把文件上传到服务器。不需要单独的API服务器,也不需要使用JS代码。它将流转换为一个文件。
@using System.IO
@inject IWebHostEnvironment env
@*for ibrowser*@
@using Microsoft.AspNetCore.Components.Forms;
<h1>Blazor Server File Upload</h1>
<h3>@Message</h3>
<form @onsubmit="OnSubmit">
<InputFile OnChange="OnInputFileChange" multiple />
<br /><br />
<button type="submit">Upload Selected File(s)</button>
</form>
@code {
string Message = "No file(s) selected";
IReadOnlyList<IBrowserFile> selectedFiles;
void OnInputFileChange(InputFileChangeEventArgs e)
{
selectedFiles = e.GetMultipleFiles();
Message = $"{selectedFiles.Count} file(s) selected";
this.StateHasChanged();
}
async void OnSubmit()
{
foreach (var file in selectedFiles)
{
Stream stream = file.OpenReadStream();
var path = $"{env.WebRootPath}\{file.Name}";
FileStream fs = File.Create(path);
await stream.CopyToAsync(fs);
stream.Close();
fs.Close();
}
Message = $"{selectedFiles.Count} file(s) uploaded on server";
this.StateHasChanged();
}
}
(对http://www.bipinjoshi.net/articles/06473cc7-a391-409e-948d-3752ba3b4a6c.aspx)