11:01 0 0
Upload archivos con Blazor

Upload archivos con Blazor

  DrUalcman |  junio 272021

He creado una pequeña utilidad para el manejo de imágenes usango el componente InputImage desde Blazor. Con esta pequeña utilidad puedes manejar la subida de imágenes tanto de una en una, como de muchas en muchas. Otra posibilidad que tiene, es que incluso si el usuario va seleccionando de una en una siempre va a mantener una lista con todas las imágenes que el usuario ha ido seleccionando, para luego subirlas todas de una sola vez al servidor. Y todo sin utilizar JAVASCRIPT ni nada por el estilo, puro C#.

Como usarlo

Primero vamos a ver como usarlo y luego paso por paso voy explicando el objeto, que he llamado UploadFilesHandler

  1. En el componente que valla a aceptar subida de ficheros, debes de hacer una instancia del objeto UploadFilesHandler e inicializarlo haciendo un override de OnInitializaed(). Para un correcto uso, deberías de, al menos, suscribirte a dos eventos OnUploadImage y OnUploadError y usar algún manejador que muestre los mensajes que vas a recibir.
  2. En el componente InputFile suscribiras el evento OnChange al método UploadImage de nuestro objeto UploadFilesHandler.
  3. En tu botón de acción para subir los ficheros llamarás al método UploadImageAsync pasándole el ENDPOINT donde subir los ficheros con el nombre del campo que debe de enviar. Tambien puedes enviar un objeto MultipartFormDataContent con todas las propiedades que necitas enviar el ENDPOINT y el nombre del campo que debe de contener el o los ficheros, ya que UploadImageAsync incluirá éste cuando valla a realizar el envío.
  4. Listo
Como puede ver, es muy simple de utilizar. Pero con muchas posibilidades, ya que como el objeto contiene un diccionario ordenado con todos los archivos que se están incluyendo, pues puedes, por ejemplo, estar mostrando al usuario todos los fichero antes de enviarlos y que elimine o agregue más, hasta llegar el límeta de ficheros de hemos definido a la hora de crear el objeto.

Un poco de código que muestra el ejemplo aquí expuesto.
< InputFile class="file-input" OnChange="Files.UploadImage" / >
< button class="button is-success" @onclick="Submit" >Save

@code{
[Inject]
public HttpClient Client { get; set; }

UploadFilesHandler Files;
string LastPictureName;

protected override void OnInitialized()
{
Files = new UploadFilesHandler(httpClient: Client, maxFiles: 1, maxSize: 5120000); //max 5Mb allowed per file, max 1 file each time
Files.OnUploadImage += SelectImage;
Files.OnUploadError += Files_OnUploadError;
}

async void Files_OnUploadError(object sender, ArgumentException e)
{
//show error to the user
Console.WriteLine(e.Message)
}

async void SelectImage(object sender, FileUploadEventArgs e)
{
LastPictureName = Files.FileName;
//show file data to the user
Console.WriteLine($"File {e.File.Name} has {e.Action}");
}

async Task Save()
{
try
{
bool result = await Files.UploadImageAsync("myEndPoint"); //if have controlled error show in the event Files_OnUploadError
}
catch (Exception ex)
{
//show error to the user
Console.WriteLine(ex.Message)
}
}
}

Configurando UploadFilesHandler

Constructor y opciones

        /// 
/// Can upload files
///

///
/// Maximum files allowed to upload
/// Maximum file size to upload
public UploadFilesHandler(HttpClient httpClient = null, int maxFiles = 5, int maxSize = 512000)
{
if (httpClient is not null) HttpClient = httpClient;

MaxAllowedFiles = maxFiles;
MaxAllowedSize = maxSize;
}
  • httpClient: Para poder utilizar los métodos para subir los archivos, pero no es obligatorio, ya que lo puedes manejar desde tu código.
  • maxFiles: Indica el número máximo de ficheros que se pueden manejar. Por defecto es 5.
  • maxSize: Indica el tamaño máximo por cada archivo. Por defecto son 500Kb.
Si por algún motivo no puedes, o no quieres, inicializar el contructor con el HttpClient, puedes luego enviarlo utilizando el método SetHttpClient(HttpClient httpClient) por lo que no hay problema de instanciar el objeto sin parámetros, utilizando todo por defecto.

        /// 
/// Set HttpClient if is not from the constructor
///

///
public void SetHttpClient(HttpClient httpClient)
{
HttpClient = httpClient;
}
Propiedades
  • this[index]: Acceso directo a un fichero por su índice. Tenemos posibilidad de por el número de índice o por el nombre de archivo.
  • First: Recupera el primer fichero.
  • Last: Recupera el último fichero.
  • Count: Devuelve el número de ficheros.
  • Size: Devuelve el la sume de bytes de todos los ficheros. 
        /// 
/// Define the indexer to allow client code to use [] notation to access directly to the file.
///

///
///
public FileUploadContent this[int index] => UploadedFiles[index];

///
/// Define the indexer to allow client code to use [] notation to access directly to the file by file name.
///

///
///
public FileUploadContent this[string fileName] => UploadedFiles.First(n=>n.Value.Name == fileName).Value;

///
/// Return first image from the dictionary
///

public FileUploadContent First
{
get
{
int c = UploadedFiles.Count;
if (c > 0)
{
return UploadedFiles[0];
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "First"));
}
return null;
}
}
}

///
/// Return last image from the dictionary
///

public FileUploadContent Last
{
get
{
int c = UploadedFiles.Count;
if (c > 0)
{
return UploadedFiles[c - 1];
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "Last"));
}
return null;
}
}
}

///
/// Return how many images have stored
///

public int Count => UploadedFiles.Count;


///
/// Return total file size uploaded
///

public long Size => UploadedFiles.Sum(s => s.Value.Size);

Campos

  • UploadedImage: Contiene el contenido del último fichero seleccionado.
  • FileName: Contiene el nombre del último fichero seleccionado.

Eventos

  • OnUploadImage: Se dispara despues de cada fichero agregado. Si el usuario ha seleccionado multiples ficheros obtendras un evento por cada fichero. Devuelve FileUploadEventArgs que contiene los dátos del fichero y su contenido.
  • OnUploaded: Se dispara después de que todos los ficheros seleccionados se han agregado. Devuelve FilesUploadEventArgs que contiene una lista de todos los archivos, la cuenta de cuántos archivos se han agregado y el total de bytes.
  • OnUploadError: Se dispara en cualquier error controlado. Devuelve un ArgumentException con la descripcion del error y el método que lo ha disparado.

Objetos devueltos por eventos

    /// 
/// Return file name and file stream per each file uploaded
///

public class FileUploadEventArgs : EventArgs
{
///
/// File uploaded with all the data
///

public FileUploadContent File { get; set; }
///
/// Index in the object
///

public int FileIndex { get; set; }
///
/// Action used
///

public string Action { get; set; }
}

///
/// Return all files uploaded
///

public class FilesUploadEventArgs : EventArgs
{
///
/// Files uploaded
///

public SortedDictionary Files { get; set; }
///
/// Total size of all the files uploated
///

public long Size { get; set; }
///
/// Number of the files uploated
///

public int Count { get; set; }
///
/// Action used
///

public string Action { get; set; }
}

Objeto que contenie el fichero

    /// 
/// Manage the file upload
///

public class FileUploadContent
{
///
/// The name of the file as specified by the browser.
///

public string Name { get; set; }
///
/// The last modified date as specified by the browser.
///

public DateTimeOffset LastModified { get; set; }
///
/// The size of the file in bytes as specified by the browser.
///

public long Size { get; set; }
///
/// The MIME type of the file as specified by the browser.
///

public string ContentType { get; set; }
///
/// File bites
///

public StreamContent FileStreamContent { get; set; }
}

Métodos

  • GetEnumerator: He ingluido un enumerador para poder hacer un Foreach del objeto directamente y manejar los archivos.
  • UploadImage: Básicamente el más importante, ya que es el utilizado desde el componente InputFile con el evento OnChange. Agrega todos los archivos seleccionado al objeto. Controla que el número de archivos seleccionados no sea mayor que el total de archvos admitidos.
  • Add: Agrega manualmente un fichero.
  • Update: Actualiza un fichero por el índice o por el nombre de archivo.
  • Remove: Elimina un archivo de la lista por el índice o el nombre.
        /// 
/// Foreach enumerator to get all the files
///

///
public IEnumerator GetEnumerator()
{
foreach (var item in UploadedFiles)
yield return item.Value;
}

///
/// Use with InputFile OnChange
///

/// InputFileChangeEventArgs
public void UploadFile(InputFileChangeEventArgs e)
{
try
{
if (e.FileCount == 0)
{
UploadedImage = null;
FileName = null;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "UploadFile"));
}
}
else
{
if (e.FileCount > this.MaxAllowedFiles)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Max files can be selected is {this.MaxAllowedFiles}", "UploadFile"));
}
}
else
{
if (this.MaxAllowedFiles == 1) //if only allowed 1 file always reset the dictionary
UploadedFiles = new SortedDictionary();

int files = UploadedFiles.Count - 1;
long size = 0;
foreach (IBrowserFile file in e.GetMultipleFiles(maximumFileCount: MaxAllowedFiles))
{
size += file.Size;
Add(new FileUploadContent
{
Name = file.Name,
LastModified = file.LastModified,
Size = file.Size,
ContentType = file.ContentType,
FileStreamContent = new StreamContent(file.OpenReadStream(maxAllowedSize: MaxAllowedSize))
});
files++;
}

if (OnUploaded is not null)
{
OnUploaded(this, new FilesUploadEventArgs { Files = UploadedFiles, Count = files, Size = size, Action = "Added" });
}
}
}
}
catch (Exception ex)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "UploadFile"));
}
}
}

#region object CRUD
///
/// Add a image
///

///
public void Add(FileUploadContent image)
{
try
{
if (image.Size < this.MaxAllowedSize)
{
if (this.MaxAllowedFiles == 1) //if only allowed 1 file always reset the dictionary
UploadedFiles = new SortedDictionary();

int index = UploadedFiles.Count;

if (index < this.MaxAllowedFiles)
{
//last image added is the default image to send
UploadedImage = image.FileStreamContent;
FileName = image.Name;

UploadedFiles.Add(index, image);
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = image, FileIndex = index, Action = "Added" });
}
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Max files is {this.MaxAllowedFiles}", "Add"));
}
}
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"File {image.Name} overload {this.MaxAllowedSize}", "Add"));
}
}
}
catch (Exception ex)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Add"));
}
}

}

///
/// Update image by index
///

///
public void Update(int index, FileUploadContent image)
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = this[index], FileIndex = index, Action = "Updating" });
}
UploadedFiles[index] = image;
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = image, FileIndex = index, Action = "Updated" });
}
}

///
/// Update image by file name
///

///
///
public bool Update(string fileName, FileUploadContent image)
{
bool result;
try
{
var file = UploadedFiles.First(i => i.Value.Name == fileName);
if (file.Value is null)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Image {fileName} not found", "UpdateImage"));
}
result = false;
}
else
{
Update(file.Key, image);
result = true;
}
}
catch (Exception ex)
{
result = false;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Update"));
}
}
return result;
}

///
/// Remove image from index
///

///
///
public bool Remove(int index)
{
FileUploadContent selection = UploadedFiles[index];
bool result = UploadedFiles.Remove(index);
if (result)
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = selection, FileIndex = index, Action = "Removed" });
}
}
else
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = selection, FileIndex = index, Action = "Remove failed" });
}
}
return result;
}

///
/// Remove image from file name
///

///
///
public bool Remove(string fileName)
{
bool result;
try
{
var file = UploadedFiles.First(i => i.Value.Name == fileName);
if (file.Value is null)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Image {fileName} not found", "RemoveImage"));
}
result = false;
}
else
{
result = Remove(file.Key);
}
}
catch (Exception ex)
{
result = false;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Remove"));
}
}
return result;

}
#endregion

Usando HttpClient

  • UploadImageAsync: Utilizarlo para el envio de los ficheros al servidor
  • DeleteImageAsync: Precisa de un ENDPOINT que devuelve un valor booleano. Envia la el nombre del fichero a eliminar de la lista por el indice. Tambien puede ser utilizado solo por el nombre, pero en este caso no lo busca en la lista.
        /// 
/// Set HttpClient if is not from the constructor
///

///
public void SetHttpClient(HttpClient httpClient)
{
HttpClient = httpClient;
}

///
/// Set IJSRuntime if it's not from the constructor
///

///
public void SetIJSRuntime(IJSRuntime jSRuntime)
{
JSRuntime = jSRuntime;
}
#endregion

#region api calls
///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, string field = "files")
{
if (UploadedImage is not null)
{
using MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(
content: UploadedImage,
name: field,
fileName: FileName
);
return await UploadFilesAsync(urlEndPoint, content, true, field);
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"No files to upload", "UploadImageAsync"));
}
return default(TModel);
}
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, InputFileChangeEventArgs files, string field = "files") =>
await UploadAsync(urlEndPoint, new MultipartFormDataContent(), files, field);

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, string field = "files")
{
if (UploadedImage is not null)
{
content.Add(
content: UploadedImage,
name: field,
fileName: FileName
);
}
return await UploadFilesAsync(urlEndPoint, content, true, field);
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, InputFileChangeEventArgs files, string field = "files")
{
UploadFile(files);
return await UploadFilesAsync(urlEndPoint, content, false, field);
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
///
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, StreamContent file, string fileName = "", string field = "files")
{
if (file is not null)
{
content.Add(
content: file,
name: field,
fileName: string.IsNullOrEmpty(fileName) ? FileName : fileName
);
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"No files to upload", "UploadImageAsync"));
}
}
return await UploadFilesAsync(urlEndPoint, content, true, field);
}

///
/// Upload all files uploaded
///

///
///
///
///
/// form content name to upload the file
///
private async Task UploadFilesAsync(string urlEndPoint, MultipartFormDataContent content,
bool ignoreFiles, string field = "files")
{
if (HttpClient is null) throw new ArgumentException("At least HttpClient Must be provided. Use HttpClient or IDefaultServices.");
if (!ignoreFiles)
{
int c = UploadedFiles.Count;
long size = 0;
for (int i = 0; i < c; i++)
{
content.Add(
content: UploadedFiles[i].FileStreamContent,
name: field,
fileName: UploadedFiles[i].Name
);
size += UploadedFiles[i].Size;
}
if (OnUploaded is not null)
{
OnUploaded(this, new FilesUploadEventArgs { Count = c, Files = UploadedFiles, Size = size });
}
}

using HttpResponseMessage result = await HttpClient.PostAsync(urlEndPoint, content);
TModel response = await result.Content.ReadFromJsonAsync();
return response;
}

///
/// Delete the image
///

/// Must be return boolean the endpoint
///
/// form content name to upload the file
///
public async Task DeleteAsync(string endPoint, int index, string field) =>
await DeleteAsync(endPoint, this[index].Name, field);

///
/// Delete the image from the filename
///

/// Must be return boolean the endpoint
///
/// form content name to upload the file
///
public async Task DeleteAsync(string endPoint, string filename, string field)
{
if (HttpClient is null) throw new ArgumentException("At least HttpClient Must be provided. Use HttpClient or IDefaultServices.");
if (string.IsNullOrEmpty(filename)) return false;

using MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StringContent(filename), field);

using HttpResponseMessage response = await HttpClient.PostAsync(endPoint, content);
return await response.Content.ReadFromJsonAsync();
}
Para los ENDPOINT protegidos por autenticación te dejo a ti como manejarlo, pero básicamente es copiar de nuevo los métodos de UploadImageAsync y llamarlos por ejemplo UploadImageAuthAsync y ponerle la lógica de envío de credenciales, o directamente cambiar estos con la lódiga de envío de credenciales si siempre lo vas a usar desde un entorno con autenticación.

Dispose

Como trabajamos con archivos no olvidar usar el método Dispose() cuando ya no se trabaje con el objeto.

Código

Aquí todo el código. Espero que os sirva de ayuda.
using ClassLibrary.Extensions;
using ClassLibrary.Service;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

namespace ClassLibrary.Handlers
{
public class UploadFilesHandler : IDisposable
{
#region variables
private HttpClient HttpClient;

int MaxAllowedFiles;
int MaxAllowedSize;

///
/// All files uploaded
///

private SortedDictionary UploadedFiles = new SortedDictionary();
#endregion

#region constructor
///
/// Can upload files
///

///
///
/// Maximum files allowed to upload
/// Maximum file size to upload
public UploadFilesHandler(HttpClient httpClient = null, IJSRuntime jSRuntime = null, int maxFiles = 5, int maxSize = 512000)
{
if (services is not null)
{
HttpClient = services.Client ?? httpClient;
JSRuntime = services.JsRuntime ?? jSRuntime;
}
if (httpClient is not null) HttpClient = httpClient;
if (jSRuntime is not null) JSRuntime = jSRuntime;

MaxAllowedFiles = maxFiles;
MaxAllowedSize = maxSize;
}
#endregion

#region properties
///
/// Define the indexer to allow client code to use [] notation to access directly to the file.
///

///
///
public FileUploadContent this[int index] => UploadedFiles[index];

///
/// Define the indexer to allow client code to use [] notation to access directly to the file by file name.
///

///
///
public FileUploadContent this[string fileName] => UploadedFiles.First(n=>n.Value.Name == fileName).Value;

///
/// Return first image from the dictionary
///

public FileUploadContent First
{
get
{
int c = UploadedFiles.Count;
if (c > 0)
{
return UploadedFiles[0];
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "First"));
}
return null;
}
}
}

///
/// Return last image from the dictionary
///

public FileUploadContent Last
{
get
{
int c = UploadedFiles.Count;
if (c > 0)
{
return UploadedFiles[c - 1];
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "Last"));
}
return null;
}
}
}

///
/// Return how many images have stored
///

public int Count => UploadedFiles.Count;

///
/// Return total file size uploaded
///

public long Size => UploadedFiles.Sum(s => s.Value.Size);
#endregion

#region fields
///
/// Last File stream uploaded
///

public StreamContent UploadedImage;
///
/// Last File name uploaded
///

public string FileName;
#endregion

#region events
public delegate Task UploadEventHandler(object sender, FileUploadEventArgs e);
public delegate Task UploadsEventHandler(object sender, FilesUploadEventArgs e);
public delegate Task UploadErrorEventHandler(object sender, ArgumentException e);

///
/// Event to notify each file uploaded
///

public event UploadEventHandler OnUploadFile;

///
/// Event to notify all files are uploaded
///

public event UploadsEventHandler OnUploaded;

///
/// Event to notify errors occurs
///

public event UploadErrorEventHandler OnUploadError;
#endregion

#region methods to manage files
///
/// Foreach enumerator to get all the files
///

///
public IEnumerator GetEnumerator()
{
foreach (var item in UploadedFiles)
yield return item.Value;
}

///
/// Use with InputFile OnChange
///

/// InputFileChangeEventArgs
public void UploadFile(InputFileChangeEventArgs e)
{
try
{
if (e.FileCount == 0)
{
UploadedImage = null;
FileName = null;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException("No images found", "UploadFile"));
}
}
else
{
if (e.FileCount > this.MaxAllowedFiles)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Max files can be selected is {this.MaxAllowedFiles}", "UploadFile"));
}
}
else
{
if (this.MaxAllowedFiles == 1) //if only allowed 1 file always reset the dictionary
UploadedFiles = new SortedDictionary();

int files = UploadedFiles.Count - 1;
long size = 0;
foreach (IBrowserFile file in e.GetMultipleFiles(maximumFileCount: MaxAllowedFiles))
{
size += file.Size;
Add(new FileUploadContent
{
Name = file.Name,
LastModified = file.LastModified,
Size = file.Size,
ContentType = file.ContentType,
FileStreamContent = new StreamContent(file.OpenReadStream(maxAllowedSize: MaxAllowedSize))
});
files++;
}

if (OnUploaded is not null)
{
OnUploaded(this, new FilesUploadEventArgs { Files = UploadedFiles, Count = files, Size = size, Action = "Added" });
}
}
}
}
catch (Exception ex)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "UploadFile"));
}
}
}

#region object CRUD
///
/// Add a image
///

///
public void Add(FileUploadContent image)
{
try
{
if (image.Size < this.MaxAllowedSize)
{
if (this.MaxAllowedFiles == 1) //if only allowed 1 file always reset the dictionary
UploadedFiles = new SortedDictionary();

int index = UploadedFiles.Count;

if (index < this.MaxAllowedFiles)
{
//last image added is the default image to send
UploadedImage = image.FileStreamContent;
FileName = image.Name;

UploadedFiles.Add(index, image);
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = image, FileIndex = index, Action = "Added" });
}
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Max files is {this.MaxAllowedFiles}", "Add"));
}
}
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"File {image.Name} overload {this.MaxAllowedSize}", "Add"));
}
}
}
catch (Exception ex)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Add"));
}
}

}

///
/// Update image by index
///

///
public void Update(int index, FileUploadContent image)
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = this[index], FileIndex = index, Action = "Updating" });
}
UploadedFiles[index] = image;
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = image, FileIndex = index, Action = "Updated" });
}
}

///
/// Update image by file name
///

///
///
public bool Update(string fileName, FileUploadContent image)
{
bool result;
try
{
var file = UploadedFiles.First(i => i.Value.Name == fileName);
if (file.Value is null)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Image {fileName} not found", "UpdateImage"));
}
result = false;
}
else
{
Update(file.Key, image);
result = true;
}
}
catch (Exception ex)
{
result = false;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Update"));
}
}
return result;
}

///
/// Remove image from index
///

///
///
public bool Remove(int index)
{
FileUploadContent selection = UploadedFiles[index];
bool result = UploadedFiles.Remove(index);
if (result)
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = selection, FileIndex = index, Action = "Removed" });
}
}
else
{
if (OnUploadFile is not null)
{
OnUploadFile(this, new FileUploadEventArgs { File = selection, FileIndex = index, Action = "Remove failed" });
}
}
return result;
}

///
/// Remove image from file name
///

///
///
public bool Remove(string fileName)
{
bool result;
try
{
var file = UploadedFiles.First(i => i.Value.Name == fileName);
if (file.Value is null)
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Image {fileName} not found", "RemoveImage"));
}
result = false;
}
else
{
result = Remove(file.Key);
}
}
catch (Exception ex)
{
result = false;
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"Exception: {ex.Message}", "Remove"));
}
}
return result;

}
#endregion
#endregion

#region methods for api calls
///
/// Set HttpClient if is not from the constructor
///

///
public void SetHttpClient(HttpClient httpClient)
{
HttpClient = httpClient;
}

///
/// Set IJSRuntime if it's not from the constructor
///

///
public void SetIJSRuntime(IJSRuntime jSRuntime)
{
JSRuntime = jSRuntime;
}
#endregion

#region api calls
///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, string field = "files")
{
if (UploadedImage is not null)
{
using MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(
content: UploadedImage,
name: field,
fileName: FileName
);
return await UploadFilesAsync(urlEndPoint, content, true, field);
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"No files to upload", "UploadImageAsync"));
}
return default(TModel);
}
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, InputFileChangeEventArgs files, string field = "files") =>
await UploadAsync(urlEndPoint, new MultipartFormDataContent(), files, field);

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, string field = "files")
{
if (UploadedImage is not null)
{
content.Add(
content: UploadedImage,
name: field,
fileName: FileName
);
}
return await UploadFilesAsync(urlEndPoint, content, true, field);
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, InputFileChangeEventArgs files, string field = "files")
{
UploadFile(files);
return await UploadFilesAsync(urlEndPoint, content, false, field);
}

///
/// Upload a image using the endpoint send
///

/// Model to use on the response from the url end point
///
/// form content to send to the url end point
///
///
/// form content name to upload the file
///
public async Task UploadAsync(string urlEndPoint, MultipartFormDataContent content, StreamContent file, string fileName = "", string field = "files")
{
if (file is not null)
{
content.Add(
content: file,
name: field,
fileName: string.IsNullOrEmpty(fileName) ? FileName : fileName
);
}
else
{
if (OnUploadError is not null)
{
OnUploadError(this, new ArgumentException($"No files to upload", "UploadImageAsync"));
}
}
return await UploadFilesAsync(urlEndPoint, content, true, field);
}

///
/// Upload all files uploaded
///

///
///
///
///
/// form content name to upload the file
///
private async Task UploadFilesAsync(string urlEndPoint, MultipartFormDataContent content,
bool ignoreFiles, string field = "files")
{
if (HttpClient is null) throw new ArgumentException("At least HttpClient Must be provided. Use HttpClient or IDefaultServices.");
if (!ignoreFiles)
{
int c = UploadedFiles.Count;
long size = 0;
for (int i = 0; i < c; i++)
{
content.Add(
content: UploadedFiles[i].FileStreamContent,
name: field,
fileName: UploadedFiles[i].Name
);
size += UploadedFiles[i].Size;
}
if (OnUploaded is not null)
{
OnUploaded(this, new FilesUploadEventArgs { Count = c, Files = UploadedFiles, Size = size });
}
}

using HttpResponseMessage result = await HttpClient.PostAsync(urlEndPoint, content);
TModel response = await result.Content.ReadFromJsonAsync();
return response;
}

///
/// Delete the image
///

/// Must be return boolean the endpoint
///
/// form content name to upload the file
///
public async Task DeleteAsync(string endPoint, int index, string field) =>
await DeleteAsync(endPoint, this[index].Name, field);

///
/// Delete the image from the filename
///

/// Must be return boolean the endpoint
///
/// form content name to upload the file
///
public async Task DeleteAsync(string endPoint, string filename, string field)
{
if (HttpClient is null) throw new ArgumentException("At least HttpClient Must be provided. Use HttpClient or IDefaultServices.");
if (string.IsNullOrEmpty(filename)) return false;

using MultipartFormDataContent content = new MultipartFormDataContent();
content.Add(new StringContent(filename), field);

using HttpResponseMessage response = await HttpClient.PostAsync(endPoint, content);
return await response.Content.ReadFromJsonAsync();
}
#endregion

#region dispose
private bool disposedValue;

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
UploadedImage?.Dispose();
int c = UploadedFiles.Count;
for (int i = 0; i < c; i++)
{
UploadedFiles[i]?.FileStreamContent?.Dispose();
}
}

disposedValue = true;
}
}

public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}

///
/// Return file name and file stream per each file uploaded
///

public class FileUploadEventArgs : EventArgs
{
///
/// File uploaded with all the data
///

public FileUploadContent File { get; set; }
///
/// Index in the object
///

public int FileIndex { get; set; }
///
/// Action used
///

public string Action { get; set; }
}

///
/// Return all files uploaded
///

public class FilesUploadEventArgs : EventArgs
{
///
/// Files uploaded
///

public SortedDictionary Files { get; set; }
///
/// Total size of all the files uploated
///

public long Size { get; set; }
///
/// Number of the files uploated
///

public int Count { get; set; }
///
/// Action used
///

public string Action { get; set; }
}

///
/// Manage the file upload
///

public class FileUploadContent
{
///
/// The name of the file as specified by the browser.
///

public string Name { get; set; }
///
/// The last modified date as specified by the browser.
///

public DateTimeOffset LastModified { get; set; }
///
/// The size of the file in bytes as specified by the browser.
///

public long Size { get; set; }
///
/// The MIME type of the file as specified by the browser.
///

public string ContentType { get; set; }
///
/// File bites
///

public StreamContent FileStreamContent { get; set; }
}
}

Happy codding

#blazor #CSHARP


0 Comentarios

 
 
 

Archivo