Hablé sobre cómo cargar imágenes en Reactjs con Asp.Net Core WebAPI. Estamos creando la API web Asp.Net Core y creamos una base de datos de servidor SQL con un núcleo de marco de entidad. y luego creé un controlador asp.net Core API para cargar imágenes.
Creamos la aplicación del lado del cliente en Reactjs. Para ello se ha diseñado un formulario con cargador de imágenes. La vista previa de la imagen seleccionada se muestra por separado. Dentro del evento de envío del formulario, cargué la imagen seleccionada en Asp.Net Web API.
Herramientas: VS Code, Visual Studio, SSMS, Postman
Cliente: reaccionarjs
API: Asp.Net Core WebAPI
Este controlador está conectado a una conexión de base de datos y proporciona métodos para enumerar, insertar, actualizar y eliminar registros de empleados mediante el manejo de solicitudes HTTP GET, POST, PUT y DELETE.
Este controlador funciona interactuando con un formulario de empleado donde los empleados pueden cargar sus imágenes. Este formulario está diseñado en el lado de React y se crea usando el comando npx create-react-app en el directorio raíz del proyecto.
Se proporcionan métodos relevantes para las operaciones GET, POST, PUT y DELETE. Con una solicitud GET, se enumeran todos los registros de empleados existentes. Una solicitud PUT actualiza un registro de empleado específico y también se utiliza para cambiar la imagen de perfil de un empleado. La solicitud POST agrega un nuevo registro de empleado y carga la foto de perfil del empleado. Una solicitud DELETE elimina un registro de empleado específico.
Además, se proporcionan dos métodos especiales, SaveImage() y DeleteImage(), para cargar y eliminar imágenes en los registros de los empleados. Estos métodos manejan la carga y eliminación de imágenes de perfil de empleados.
using System ;
using System . Collections . Generic ;
using System . Linq ;
using System . Threading . Tasks ;
using Microsoft . AspNetCore . Http ;
using Microsoft . AspNetCore . Mvc ;
using Microsoft . EntityFrameworkCore ;
using EmployeeRegisterAPI . Models ;
using System . IO ;
using Microsoft . AspNetCore . Hosting ;
namespace EmployeeRegisterAPI . Controllers
{
[ Route ( "api/[controller]" ) ]
[ ApiController ]
public class EmployeeController : ControllerBase
{
private readonly EmployeeDbContext _context ;
private readonly IWebHostEnvironment _hostEnvironment ;
public EmployeeController ( EmployeeDbContext context , IWebHostEnvironment hostEnvironment )
{
_context = context ;
this . _hostEnvironment = hostEnvironment ;
}
// GET: api/EmployeeModels
[ HttpGet ]
public async Task < ActionResult < IEnumerable < EmployeeModel > > > GetEmployees ( )
{
return await _context . Employees
. Select ( x => new EmployeeModel ( )
{
EmployeeID = x . EmployeeID ,
EmployeeName = x . EmployeeName ,
Occupation = x . Occupation ,
ImageName = x . ImageName ,
ImageSrc = String . Format ( "{0}://{1}{2}/Images/{3}" , Request . Scheme , Request . Host , Request . PathBase , x . ImageName )
} )
. ToListAsync ( ) ;
}
// GET: api/Employee/5
[ HttpGet ( "{id}" ) ]
public async Task < ActionResult < EmployeeModel > > GetEmployeeModel ( int id )
{
var employeeModel = await _context . Employees . FindAsync ( id ) ;
if ( employeeModel == null )
{
return NotFound ( ) ;
}
return employeeModel ;
}
// PUT: api/Employee/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
[ HttpPut ( "{id}" ) ]
public async Task < IActionResult > PutEmployeeModel ( int id , [ FromForm ] EmployeeModel employeeModel )
{
if ( id != employeeModel . EmployeeID )
{
return BadRequest ( ) ;
}
if ( employeeModel . ImageFile != null )
{
DeleteImage ( employeeModel . ImageName ) ;
employeeModel . ImageName = await SaveImage ( employeeModel . ImageFile ) ;
}
_context . Entry ( employeeModel ) . State = EntityState . Modified ;
try
{
await _context . SaveChangesAsync ( ) ;
}
catch ( DbUpdateConcurrencyException )
{
if ( ! EmployeeModelExists ( id ) )
{
return NotFound ( ) ;
}
else
{
throw ;
}
}
return NoContent ( ) ;
}
// POST: api/Employee
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
[ HttpPost ]
public async Task < ActionResult < EmployeeModel > > PostEmployeeModel ( [ FromForm ] EmployeeModel employeeModel )
{
employeeModel . ImageName = await SaveImage ( employeeModel . ImageFile ) ;
_context . Employees . Add ( employeeModel ) ;
await _context . SaveChangesAsync ( ) ;
return StatusCode ( 201 ) ;
}
// DELETE: api/Employee/5
[ HttpDelete ( "{id}" ) ]
public async Task < ActionResult < EmployeeModel > > DeleteEmployeeModel ( int id )
{
var employeeModel = await _context . Employees . FindAsync ( id ) ;
if ( employeeModel == null )
{
return NotFound ( ) ;
}
DeleteImage ( employeeModel . ImageName ) ;
_context . Employees . Remove ( employeeModel ) ;
await _context . SaveChangesAsync ( ) ;
return employeeModel ;
}
private bool EmployeeModelExists ( int id )
{
return _context . Employees . Any ( e => e . EmployeeID == id ) ;
}
[ NonAction ]
public async Task < string > SaveImage ( IFormFile imageFile )
{
string imageName = new String ( Path . GetFileNameWithoutExtension ( imageFile . FileName ) . Take ( 10 ) . ToArray ( ) ) . Replace ( ' ' , '-' ) ;
imageName = imageName + DateTime . Now . ToString ( "yymmssfff" ) + Path . GetExtension ( imageFile . FileName ) ;
var imagePath = Path . Combine ( _hostEnvironment . ContentRootPath , "Images" , imageName ) ;
using ( var fileStream = new FileStream ( imagePath , FileMode . Create ) )
{
await imageFile . CopyToAsync ( fileStream ) ;
}
return imageName ;
}
[ NonAction ]
public void DeleteImage ( string imageName )
{
var imagePath = Path . Combine ( _hostEnvironment . ContentRootPath , "Images" , imageName ) ;
if ( System . IO . File . Exists ( imagePath ) )
System . IO . File . Delete ( imagePath ) ;
}
}
}
El formulario permite al usuario agregar o editar un empleado. El componente proporciona un formulario donde puede cargar una imagen e ingresar información sobre el nombre y el trabajo del empleado. El formulario consta de un área de carga de imágenes y dos campos de entrada de texto.
Se utilizan muchas variables. La variable defaultImageSrc contiene la ruta de la imagen predeterminada. La variable inicialFieldValues contiene los valores iniciales que se utilizarán en el estado inicial del componente. Con useState, se realiza un seguimiento de los estados de los valores dentro del componente.
La función useEffect se utiliza durante la carga del componente y cuando la variable recordForEdit cambia, activa la recarga del componente y la variable de valores se actualiza con la función setValues.
La función handleInputChange se llama cuando se cambian los campos de entrada de texto. Esta función captura el nombre y el valor del campo modificado usando e.target.name y e.target.value y actualiza la variable de valores usando la función setValues.
La función showPreview se llama si se cambia el área de carga de la imagen. Esta función guarda la ruta del archivo seleccionado en la variable imageFile y crea una vista previa del archivo usando el objeto FileReader y actualiza la variable imageSrc.
La función de validación verifica la exactitud del formulario. Esta función se utiliza para garantizar que la variable de valores esté completa correctamente. La función setErrors actualiza la variable de errores y la función resetForm restablece el formulario y borra la variable de errores.
La función handleFormSubmit se llama cuando se envía el formulario. Esta función recupera los datos del formulario utilizando el objeto FormData y envía los datos al servidor llamando a la función addOrEdit.
La función applyErrorClass aplica la clase de campo no válido a campos erróneos, lo que provoca que estos campos se muestren con errores.
El componente proporciona un formulario donde puede cargar una imagen e ingresar información sobre el nombre y el trabajo del empleado. El formulario consta de un área de carga de imágenes y dos campos de entrada de texto.
import React , { useState , useEffect } from 'react'
const defaultImageSrc = '/img/3135715.png'
const initialFieldValues = {
employeeID : 0 ,
employeeName : '' ,
occupation : '' ,
imageName : '' ,
imageSrc : defaultImageSrc ,
imageFile : null
}
export default function Employee ( props ) {
const { addOrEdit , recordForEdit } = props
const [ values , setValues ] = useState ( initialFieldValues )
const [ errors , setErrors ] = useState ( { } )
useEffect ( ( ) => {
if ( recordForEdit != null )
setValues ( recordForEdit ) ;
} , [ recordForEdit ] )
const handleInputChange = e => {
const { name , value } = e . target ;
setValues ( {
... values ,
[ name ] : value
} )
}
const showPreview = e => {
if ( e . target . files && e . target . files [ 0 ] ) {
let imageFile = e . target . files [ 0 ] ;
const reader = new FileReader ( ) ;
reader . onload = x => {
setValues ( {
... values ,
imageFile ,
imageSrc : x . target . result
} )
}
reader . readAsDataURL ( imageFile )
}
else {
setValues ( {
... values ,
imageFile : null ,
imageSrc : defaultImageSrc
} )
}
}
const validate = ( ) => {
let temp = { }
temp . employeeName = values . employeeName == "" ? false : true ;
temp . imageSrc = values . imageSrc == defaultImageSrc ? false : true ;
setErrors ( temp )
return Object . values ( temp ) . every ( x => x == true )
}
const resetForm = ( ) => {
setValues ( initialFieldValues )
document . getElementById ( 'image-uploader' ) . value = null ;
setErrors ( { } )
}
const handleFormSubmit = e => {
e . preventDefault ( )
if ( validate ( ) ) {
const formData = new FormData ( )
formData . append ( 'employeeID' , values . employeeID )
formData . append ( 'employeeName' , values . employeeName )
formData . append ( 'occupation' , values . occupation )
formData . append ( 'imageName' , values . imageName )
formData . append ( 'imageFile' , values . imageFile )
addOrEdit ( formData , resetForm )
}
}
const applyErrorClass = field => ( ( field in errors && errors [ field ] == false ) ? ' invalid-field' : '' )
return (
< >
< div className = "container text-center" >
< p className = "lead" > </ p >
</ div >
< form autoComplete = "off" noValidate onSubmit = { handleFormSubmit } >
< div className = "card" style = { { backgroundColor : '#ced114' } } >
< img src = { values . imageSrc } className = "card-img-top" />
< div className = "card-body" >
< div className = "form-group" >
< input type = "file" accept = "image/*" className = { "form-control-file" + applyErrorClass ( 'imageSrc' ) }
onChange = { showPreview } id = "image-uploader" />
</ div >
< div className = "form-group" style = { { backgroundColor : '#ced114' } } >
< input className = { "form-control" + applyErrorClass ( 'employeeName' ) } placeholder = "Employee Name" name = "employeeName"
value = { values . employeeName }
onChange = { handleInputChange } />
</ div >
< div className = "form-group" >
< input className = "form-control" placeholder = "Occupation" name = "occupation"
value = { values . occupation }
onChange = { handleInputChange } />
</ div >
< div className = "form-group text-center" >
< button type = "submit" className = "btn btn-light" style = { { color : '#ced114' } } > Submit </ button >
</ div >
</ div >
</ div >
</ form >
</ >
)
}
Este componente de React maneja la creación, edición, eliminación y visualización de un registro de empleado.
Utiliza los ganchos useState y useEffect React. useState define una variable de estado para el estado del componente y devuelve setState. useEffect ejecuta una función en respuesta a eventos como cargar o volver a dibujar el componente.
Axios se utiliza para realizar llamadas a servicios web RESTful. Una función auxiliar llamada EmployeeAPI crea instancias de llamadas RESTful y puede cambiar la URL del recurso utilizando las funciones de Axios.
La función actualizarEmployeeList recupera registros de empleados del servidor y los establece como el estado del componente con setEmployeeList.
La función addOrEdit se encarga de crear o actualizar el registro del empleado. El objeto FormData contiene entradas de usuario para el registro de empleado.
La función onDelete le pide al usuario que confirme si desea eliminar un registro y luego envía una solicitud DELETE al servidor.
La función imageCard se utiliza para mostrar las tarjetas de los empleados. La tarjeta incluye la foto del empleado, el nombre, la descripción del trabajo y un botón de eliminación.
Finalmente, el componente incluye un encabezado jumbotron, un formulario de empleado y una tabla de tarjetas de empleado. La tabla muestra tarjetas de empleados con tres columnas y filas adicionales donde la tercera columna no está ocupada.
import React , { useState , useEffect } from 'react'
import Employee from './Employee'
import axios from "axios" ;
export default function EmployeeList ( ) {
const [ employeeList , setEmployeeList ] = useState ( [ ] )
const [ recordForEdit , setRecordForEdit ] = useState ( null )
useEffect ( ( ) => {
refreshEmployeeList ( ) ;
} , [ ] )
const employeeAPI = ( url = 'https://localhost:44334/api/Employee/' ) => {
return {
fetchAll : ( ) => axios . get ( url ) ,
create : newRecord => axios . post ( url , newRecord ) ,
update : ( id , updatedRecord ) => axios . put ( url + id , updatedRecord ) ,
delete : id => axios . delete ( url + id )
}
}
function refreshEmployeeList ( ) {
employeeAPI ( ) . fetchAll ( )
. then ( res => {
setEmployeeList ( res . data )
} )
. catch ( err => console . log ( err ) )
}
const addOrEdit = ( formData , onSuccess ) => {
if ( formData . get ( 'employeeID' ) == "0" )
employeeAPI ( ) . create ( formData )
. then ( res => {
onSuccess ( ) ;
refreshEmployeeList ( ) ;
} )
. catch ( err => console . log ( err ) )
else
employeeAPI ( ) . update ( formData . get ( 'employeeID' ) , formData )
. then ( res => {
onSuccess ( ) ;
refreshEmployeeList ( ) ;
} )
. catch ( err => console . log ( err ) )
}
const showRecordDetails = data => {
setRecordForEdit ( data )
}
const onDelete = ( e , id ) => {
e . stopPropagation ( ) ;
if ( window . confirm ( 'Are you sure to delete this record?' ) )
employeeAPI ( ) . delete ( id )
. then ( res => refreshEmployeeList ( ) )
. catch ( err => console . log ( err ) )
}
const imageCard = data => (
< div className = "card" onClick = { ( ) => { showRecordDetails ( data ) } } >
< img src = { data . imageSrc } className = "card-img-top rounded-circle" />
< div className = "card-body" >
< h5 > { data . employeeName } </ h5 >
< span > { data . occupation } </ span > < br />
< button className = "btn btn-light delete-button" onClick = { e => onDelete ( e , parseInt ( data . employeeID ) ) } >
< i className = "far fa-trash-alt" > </ i >
</ button >
</ div >
</ div >
)
return (
< div className = "row" >
< div className = "col-md-12" style = { { backgroundColor : '#ced114' } } >
< div className = "jumbotron jumbotron-fluid py-4" style = { { backgroundColor : '#ced114' } } >
< div className = "container text-center" style = { { backgroundColor : '#ced114' } } >
< h1 className = "display-4" style = { { backgroundColor : '#ced114' , color : 'yellow' } } > Employee Register </ h1 >
</ div >
</ div >
</ div >
< div className = "container text-center" >
< p className = "lead" > </ p >
</ div >
< div className = "col-md-4" >
< Employee
addOrEdit = { addOrEdit }
recordForEdit = { recordForEdit }
/>
</ div >
< div className = "col-md-8" >
< table >
< tbody >
{
//tr > 3 td
[ ... Array ( Math . ceil ( employeeList . length / 3 ) ) ] . map ( ( e , i ) =>
< tr key = { i } >
< td > { imageCard ( employeeList [ 3 * i ] ) } </ td >
< td > { employeeList [ 3 * i + 1 ] ? imageCard ( employeeList [ 3 * i + 1 ] ) : null } </ td >
< td > { employeeList [ 3 * i + 2 ] ? imageCard ( employeeList [ 3 * i + 2 ] ) : null } </ td >
</ tr >
)
}
</ tbody >
</ table >
</ div >
</ div >
)
}
Estoy usando .Net Core 6.0 en mi proyecto. Los paquetes Nuget que deben descargarse en el proyecto se enumeran a continuación.
Microsoft.VisualStudio.Web.CodeGeneration.Design
PM > NuGet I nstall-Package Microsoft.VisualStudio.Web.CodeGeneration.Design -Version 6.0.13
Microsoft.EntityFrameworkCore
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore -Version 7.0.4
Microsoft.EntityFrameworkCore.SqlServer
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore.SqlServer -Version 7.0.4
Microsoft.EntityFrameworkCore.Tools
PM > NuGet I nstall-Package Microsoft.EntityFrameworkCore.Tools -Version 7.0.4
Microsoft.AspNetCore.Cors
PM > NuGet I nstall-Package Microsoft.AspNetCore.Cors -Version 2.2.0
En primer lugar, agreguemos una clase a nuestra carpeta Modelos. Lo llamé Modelo de empleado.
public class EmployeeModel
{
[ Key ]
public int EmployeeID { get ; set ; }
[ Column ( TypeName = "nvarchar(50)" ) ]
public string EmployeeName { get ; set ; }
[ Column ( TypeName = "nvarchar(50)" ) ]
public string Occupation { get ; set ; }
[ Column ( TypeName = "nvarchar(100)" ) ]
public string ImageName { get ; set ; }
}
Creemos una clase de contexto en nuestra carpeta Modelos.
using Microsoft . EntityFrameworkCore ;
namespace EmployeeRegisterAPI . Models
{
public class EmployeeDbContext : DbContext //burada ef Dbcontext den kalitim aldirdik
{
public EmployeeDbContext ( DbContextOptions < EmployeeDbContext > options ) : base ( options )
{
}
public DbSet < EmployeeModel > Employees { get ; set ; }
}
}
Vayamos a la carpeta Startup.cs y creemos una cadena de conexión para el método ConfigureServices.
public void ConfigureServices ( IServiceCollection services )
{
services . AddControllers ( ) ;
//Baglanti dizesini olusturduk ve bu kodda sql server icin bir ConnectionString olusturduk "DevConnection" diye bunula baglanti bilgilerimizi appsettings de belirtecegiz
services . AddDbContext < EmployeeDbContext > ( options => options . UseSqlServer ( Configuration . GetConnectionString ( "DevConnection" ) ) ) ;
}
Vayamos a appsettings.json. Aquí escribimos la información de conexión en el archivo json de esta manera.
"Logging" : {
"LogLevel" : {
"Default" : "Information" ,
"Microsoft" : "Warning" ,
"Microsoft.Hosting.Lifetime" : "Information"
}
} ,
"AllowedHosts" : "*" ,
//buradan sonraki kod satirlaridir
"ConnectionStrings" : {
"DevConnection" : "Server=LAPTOP-6OAEM3JA; Database=EmployeeDB; Trusted_Connection=True; MultipleActiveResultSets=True;"
}
}
Luego, construimos nuestro proyecto, vamos al administrador de paquetes y realizamos nuestras operaciones de migración.
PM > Add-Migration " InitialCreate "
Creemos Migración con el comando y construyamos nuestro proyecto. Luego, demos este comando.
PM > update-database
Luego verifiquemos si la base de datos se ha creado o no desde MSSQL.
Y luego creemos un controlador para nuestro modelo. Usaré el controlador API con acciones usando el controlador EF.
Las fotos vendrán aquí, 2 de ellas.
Creamos nuestros controles, especificamos nuestro modelo y archivo de contexto, y especificamos nuestro nombre de controlador.
5 Estos servicios se denominan AddDb.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DevConnection"))); La función creará una solicitud para este controlador de empleado cada vez que hagamos una solicitud a este EmployeeController. El constructor de EmployeeController es public EmployeeController(EmployeeDbContext context). El valor de EmployeeDbContext en este código es este método de inyección de dependencia de asp.net, que es administrado automáticamente por el propio marco, por lo que la solicitud en este controlador se comunicará con la base de datos por ahora.
Ahora, diseñemos el lado del Cliente para que reaccione con un formulario de empleado para cargar imágenes de perfil de los empleados. Creemos una aplicación de reacción en el directorio del proyecto. Necesitamos abrir el símbolo del sistema desde el directorio de este proyecto.
npx create-react-app employee-register-client
Comando Oxios que se utilizará para el lado del cliente.
$ npm install react-axios
Hemos creado la estructura de nuestros proyectos. De aquí en adelante, quiero que revises mis códigos en mi repositorio. Buena suerte :)
GET /api/Employee
Parámetro | Medicamento | Explicación |
---|---|---|
api_key | string | Necesario . Su clave API. |
GET /api/Employee/${id}
Parámetro | Medicamento | Explicación |
---|---|---|
id | string | Necesario . Valor clave del elemento a llamar |
{ "employeeID" : 3 , "employeeName" : " piedhorse " , "occupation" : " C# " , "imageName" : " capture_20231646962.jpeg " , "imageFile" : null , "imageSrc" : " https://localhost:44334/Images/capture_20231646962.jpeg " }