简体   繁体   English

如何使用 .NET 5 在 Blazor 中上传文件

[英]How can I upload files in Blazor with .NET 5

I have the following code (see below).我有以下代码(见下文)。

What I am trying to achieve is the following: I want to get the infos like size, name, etc, and after click the submit button...我想要实现的是以下内容:我想获取大小、名称等信息,然后单击提交按钮...

In the complete application would be the possibility of deleting a file in the list before triggering the upload.在完整的应用程序中,可以在触发上传之前删除列表中的文件。

This works fine EXCEPT if I submit one file after another.这工作正常,除非我提交一个又一个文件。 I get the following exception:我收到以下异常:

Error: Microsoft.JSInterop.JSException: There is no file with ID 1. The file list may have changed.错误:Microsoft.JSInterop.JSException:没有 ID 为 1 的文件。文件列表可能已更改。 Error: There is no file with ID 1. The file list may have changed.错误:没有 ID 为 1 的文件。文件列表可能已更改。

Could anyone help me?有人可以帮助我吗?

Many thanks,非常感谢,

Raphaël拉斐尔

@page "/"

<h3>Upload PNG images</h3>

<p>
    <InputFile OnChange="@OnInputFileChange" multiple />
</p>

@if (imageDataUrls.Count > 0)
{
    <h4>Images</h4>

    <div class="card" style="width:30rem;">
        <div class="card-body">
            @foreach (var imageDataUrl in imageDataUrls)
            {
                <img class="rounded m-1" src="@imageDataUrl" />
            }
        </div>
    </div>
}

@if (list.Count() > 0)
{
    <div class="card" style="width:30rem;">
        <div class="card-body">
            @foreach (var file in list)
            {
                <div>@file.Name - @file.ContentType - @file.Size</div>
            }
        </div>
    </div>
}



<button @onclick="Submit">Télécharger</button>

@code {
    
    IList<string> imageDataUrls = new List<string>();
    List<IBrowserFile> list = new List<IBrowserFile>();

    void OnInputFileChange(InputFileChangeEventArgs e)
    {
        var maxAllowedFiles = 3;

        list.AddRange(e.GetMultipleFiles(maxAllowedFiles));
    }

    async Task Submit()
    {        
        var format = "image/png";
        
        foreach (var imageFile in list)
        {   
            var resizedImageFile = await imageFile.RequestImageFileAsync(format,
                100, 100);
            var buffer = new byte[resizedImageFile.Size];
            await resizedImageFile.OpenReadStream().ReadAsync(buffer);
            var imageDataUrl =
                $"data:{format};base64,{Convert.ToBase64String(buffer)}";
            imageDataUrls.Add(imageDataUrl);
        }
        
    }


}

Nasty workaround.讨厌的解决方法。 Would not recommend with large and many files.不建议使用大文件。 Especially in server side尤其是在服务器端

Here is same issue on BlazorInputFile github How to upload multiple files on form submit这是 BlazorInputFile github 上的相同问题 如何在表单提交时上传多个文件

private async Task Save()
{
    foreach (var addedFile in _addedFiles)
    {
        using var stream = new MemoryStream(addedFile);
        var fileContent = new StreamContent(stream);
        var response = await new WebClient.PostAsync($"file", fileContent);
    }
}

private List<byte[]> _addedFiles = new List<byte[]>();
private async Task SingleUpload(InputFileChangeEventArgs arg)
{
    long maxFileSize = 1024 * 1024 * 15;
    var data = arg.File.OpenReadStream(maxFileSize);
    using var memoryString = new MemoryStream();
    await data.CopyToAsync(memoryString);

    _addedFiles.Add(memoryString.GetBuffer());
    model.Files.Add(new FileModel(arg.File.Name));
}

I don't have a really good answer to why your solution is not working, but it is easy to fix it.对于您的解决方案为何不起作用,我没有很好的答案,但很容易修复它。 It seems like you have only access to file content when you are in the event-handling method.当您处于事件处理方法中时,您似乎只能访问文件内容。

As a workaround, create a "ViewModel" class for your image presentation.作为一种解决方法,为您的图像演示创建一个“ViewModel”类。

 public class FileViewModel
{
        /// helpful in combination with @key to make list rendering more efficient
        public Guid Id { get; set; }
        public String Name { get; set; }
        public String ContentType { get; set; }
        public Int64 Size { get; set; }
        public Byte[] Content { get; set; }
        public String ImageDataUrl { get; set; }

        public FileViewModel()
        {
            Id = Guid.NewGuid();
        }

}

Changes to OnInputFileChange对 OnInputFileChange 的更改

Merge your code from Submit with OnInputFileChange .Submit的代码与OnInputFileChange合并。 During the event handling, create a ViewModel instance for each file and save the instance's content.在事件处理期间,为每个文件创建一个 ViewModel 实例并保存实例的内容。


    List<FileViewModel> list = new List<FileViewModel>();

    private async Task OnInputFileChange(InputFileChangeEventArgs e)
    {
        var maxAllowedFiles = 3;

        var format = "image/png";

        foreach (var imageFile in e.GetMultipleFiles(maxAllowedFiles))
        {
            var resizedImageFile = await imageFile.RequestImageFileAsync(format,
                100, 100);
            var buffer = new byte[resizedImageFile.Size];
            await resizedImageFile.OpenReadStream().ReadAsync(buffer);
            var imageDataUrl =
                $"data:{format};base64,{Convert.ToBase64String(buffer)}";

            list.Add(new FileViewModel
            {
                Content = buffer,
                ImageDataUrl = imageDataUrl,
                ContentType = imageFile.ContentType,
                Name = imageFile.Name,
                Size = imageFile.Size,
            });
        }
    }

I added a remove button to showcase a manipulation of the collection after the content is read.我添加了一个删除按钮来展示读取内容后对集合的操作。

delete images删除图像

@if (list.Count > 0)
{
    <h4>Images</h4>

    <div class="card" style="width:30rem;">
        <div class="card-body">
            @foreach (var file in list)
            {
                <img class="rounded m-1" @key="file.Id" src="@file.ImageDataUrl" />
                <button type="button" @onclick="() => RemoveImage(file)">Remove</button>
            }
        </div>
    </div>
}


@code {

    private void RemoveImage(FileViewModel file)
    {
        list.Remove(file);
    }
}

Using the detour, you now have full access to the data from your ViewModel instance.使用绕道,您现在可以完全访问 ViewModel 实例中的数据。 You can use these data to submit your file to your service then.然后您可以使用这些数据将您的文件提交给您的服务。 In the example, I log the file name and length of the byte array as a proof of concept.在示例中,我记录了字节数组的文件名和长度作为概念证明。


    async Task Submit()
    {
        foreach (var item in list)
        {
            Console.WriteLine($"file {item.Name} | size {item.Content.Length}");
        }
    }

entire component整个组件

@page "/"

<h3>Upload PNG images</h3>

<p>
    <InputFile OnChange="@OnInputFileChange" multiple />
</p>

@if (list.Count > 0)
{
    <h4>Images</h4>

    <div class="card" style="width:30rem;">
        <div class="card-body">
            @foreach (var file in list)
            {
                <img class="rounded m-1" @key="file.Id" src="@file.ImageDataUrl" />
                <button type="button" @onclick="() => RemoveImage(file)">Remove</button>
            }
        </div>
    </div>
}

@if (list.Count() > 0)
{
    <div class="card" style="width:30rem;">
        <div class="card-body">
            @foreach (var file in list)
            {
                <div>@file.Name - @file.ContentType - @file.Size</div>
            }
        </div>
    </div>
}



<button @onclick="Submit">Télécharger</button>

@code {

    List<FileViewModel> list = new List<FileViewModel>();

    private async Task OnInputFileChange(InputFileChangeEventArgs e)
    {
        var maxAllowedFiles = 3;

        var format = "image/png";

        foreach (var imageFile in e.GetMultipleFiles(maxAllowedFiles))
        {
            var resizedImageFile = await imageFile.RequestImageFileAsync(format,
                100, 100);
            var buffer = new byte[resizedImageFile.Size];
            await resizedImageFile.OpenReadStream().ReadAsync(buffer);
            var imageDataUrl =
                $"data:{format};base64,{Convert.ToBase64String(buffer)}";

            list.Add(new FileViewModel
            {
                Content = buffer,
                ImageDataUrl = imageDataUrl,
                ContentType = imageFile.ContentType,
                Name = imageFile.Name,
                Size = imageFile.Size,
            });
        }
    }

    private void RemoveImage(FileViewModel file)
    {
        list.Remove(file);
    }

    async Task Submit()
    {
        foreach (var item in list)
        {
            Console.WriteLine($"file {item.Name} | size {item.Content.Length}");
        }
    }

}

你有没有看过 Blazorise 中的File组件,

Once you leave the OnInputFileChange handler method, you will lose the stream to the files.一旦离开OnInputFileChange处理程序方法,您将丢失文件的流。 So you have to Read the stream and save it somewhere before you leave.因此,您必须在离开之前阅读流并将其保存在某处。 Something like this worked for me.像这样的东西对我有用。 Must be done in the OnInputFileChange event handler必须在OnInputFileChange事件处理程序中完成

List<byte[]> list = new List<byte[]>();

void OnInputFileChange(InputFileChangeEventArgs e)
{
    var maxAllowedFiles = 3;

    foreach (var imageFile in e.GetMultipleFiles(maxAllowedFiles))
    {
        var resizedImageFile = await imageFile.RequestImageFileAsync(format,100, 100);
        var buffer = new byte[resizedImageFile.Size];
        await resizedImageFile.OpenReadStream().ReadAsync(buffer);
        list.Add(buffer);
    }
}

async Task Submit()
{        
    var format = "image/png";
    
    foreach (var buffer in list)
    {   
        var imageDataUrl = $"data:{format};base64,{Convert.ToBase64String(buffer)}";
        imageDataUrls.Add(imageDataUrl);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM