Discuti como carregar imagens em Reactjs com Asp.Net Core WebAPI. Estamos criando a API da web Asp.Net Core e criamos um banco de dados SQL Server com um núcleo de estrutura de entidade. e então criei um controlador asp.net Core API para carregamento de imagens.
Criamos a aplicação do lado cliente em Reactjs. Um formulário com uploader de imagens foi projetado para isso. A visualização da imagem selecionada é mostrada separadamente. Dentro do evento de envio do formulário, carreguei a imagem selecionada na API Web Asp.Net.
Ferramentas: Código VS, Visual Studio, SSMS, Postman
Cliente: Reactjs
API: WebAPI Asp.Net Core
Este controlador está conectado a uma conexão de banco de dados e fornece métodos para listar, inserir, atualizar e excluir registros de funcionários manipulando solicitações HTTP GET, POST, PUT e DELETE.
Este controlador funciona interagindo com um formulário de funcionário onde os funcionários podem fazer upload de suas imagens. Este formulário foi projetado no lado do React e criado usando o comando npx create-react-app no diretório raiz do projeto.
Métodos relevantes são fornecidos para operações GET, POST, PUT e DELETE. Com uma solicitação GET, todos os registros de funcionários existentes são listados. Uma solicitação PUT atualiza o registro de um funcionário específico e também é usada para alterar a foto do perfil de um funcionário. A solicitação POST adiciona um novo registro de funcionário e carrega a foto do perfil do funcionário. Uma solicitação DELETE exclui um registro de funcionário especificado.
Além disso, dois métodos especiais, SaveImage() e DeleteImage(), são fornecidos para carregar e excluir imagens em registros de funcionários. Esses métodos lidam com o upload e a exclusão de imagens de perfil de funcionários.
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 ) ; } } }
O formulário permite ao usuário adicionar ou editar um funcionário. O componente fornece um formulário onde você pode fazer upload de uma imagem e inserir informações sobre o nome e cargo do funcionário. O formulário consiste em uma área de upload de imagens e dois campos de entrada de texto.
Muitas variáveis são usadas. A variável defaultImageSrc contém o caminho da imagem padrão. A variável InitialFieldValues contém os valores iniciais a serem usados no estado inicial do componente. Usando useState, os estados dos valores são rastreados dentro do componente.
A função useEffect é utilizada durante o carregamento do componente e quando a variável recordForEdit muda, aciona o recarregamento do componente e a variável valores é atualizada com a função setValues.
A função handleInputChange é chamada quando os campos de entrada de texto são alterados. Esta função captura o nome e o valor do campo alterado usando e.target.name e e.target.value e atualiza a variável de valores usando a função setValues.
A função showPreview é chamada se a área de upload da imagem for alterada. Esta função salva o caminho do arquivo selecionado na variável imageFile e cria uma visualização do arquivo usando o objeto FileReader e atualiza a variável imageSrc.
A função de validação verifica a exatidão do formulário. Esta função é usada para garantir que a variável de valores seja preenchida corretamente. A função setErrors atualiza a variável de erros e a função resetForm redefine o formulário e limpa a variável de erros.
A função handleFormSubmit é chamada quando o formulário é enviado. Esta função recupera os dados do formulário usando o objeto FormData e envia os dados para o servidor chamando a função addOrEdit.
A função applyErrorClass aplica a classe de campo inválido a campos errados, fazendo com que esses campos sejam exibidos com erros.
O componente fornece um formulário onde você pode fazer upload de uma imagem e inserir informações sobre o nome e cargo do funcionário. O formulário consiste em uma área de upload de imagens e dois 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 React cuida da criação, edição, exclusão e visualização de um registro de funcionário.
Ele usa os ganchos useState e useEffect React. useState define uma variável de estado para o estado do componente e retorna setState. useEffect executa uma função em resposta a eventos como carregar ou redesenhar o componente.
Axios é usado para fazer chamadas de serviços da web RESTful. Uma função auxiliar chamada EmployeeAPI instancia chamadas RESTful e pode alterar o URL do recurso usando os recursos do Axios.
A função refreshEmployeeList recupera registros de funcionários do servidor e os define como o estado do componente com setEmployeeList.
A função addOrEdit cuida da criação ou atualização do registro do funcionário. O objeto FormData contém entradas de usuário para o registro do funcionário.
A função onDelete pede ao usuário para confirmar se deseja excluir um registro e então envia uma solicitação DELETE ao servidor.
A função imageCard é usada para exibir os cartões dos funcionários. O cartão inclui a foto do funcionário, o nome, a descrição do cargo e um botão de exclusão.
Finalmente, o componente inclui um cabeçalho jumbotron, um formulário de funcionário e uma tabela de cartões de funcionários. A tabela exibe cartões de funcionários com três colunas e linhas adicionais onde a terceira coluna não está ocupada.
{imageCard(employeeList[3 * i])} | {employeeList[3 * i + 1] ? imageCard(employeeList[3 * i + 1]) : null} | {employeeList[3 * i + 2] ? imageCard(employeeList[3 * i + 2]) : null} |
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 >
)
}
Estou usando o .Net Core 6.0 em meu projeto. Os pacotes Nuget que precisam ser baixados no projeto estão listados abaixo.
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
Primeiro de tudo, vamos adicionar uma classe à nossa pasta Models. Eu o chamei de EmployeeModel.
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 ; }
}
Vamos criar uma classe Context em nossa pasta Models.
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 ; }
}
}
Vamos para a pasta Startup.cs e criar uma string de conexão para o 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" ) ) ) ;
}
Vamos para appsettings.json. Aqui escrevemos as informações de conexão no arquivo json assim.
"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;"
}
}
Depois construímos nosso projeto, vamos ao gerenciador de pacotes e realizamos nossas operações de migração.
PM > Add-Migration " InitialCreate "
Vamos criar Migration com o comando e construir nosso projeto. Então dê este comando.
PM > update-database
Então vamos verificar se o banco de dados foi criado ou não a partir de MSSQL.
E então vamos criar um controlador para nosso modelo. Usarei o API Controller com ações usando o controlador EF.
As fotos virão aqui, 2 delas
Criamos nossos controles, especificamos nosso modelo e arquivo de contexto e especificamos nosso nome de controlador.
5 Esses serviços chamados AddDb.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DevConnection"))); A função criará uma solicitação para este controlador de funcionário sempre que fizermos uma solicitação para este EmployeeController. O construtor do EmployeeController é public EmployeeController (contexto EmployeeDbContext). O valor EmployeeDbContext neste código é este método de injeção de dependência asp.net, que é gerenciado automaticamente pelo próprio framework, portanto, a solicitação neste controlador se comunicará com o banco de dados por enquanto.
Agora, vamos projetar o lado do cliente para que ele reaja com um formulário de funcionário para fazer upload de fotos de perfil de funcionário. Vamos criar um aplicativo react no diretório do projeto. Precisamos abrir o prompt de comando no diretório deste projeto.
npx create-react-app employee-register-client
Comando Oxios a ser usado para o lado do cliente
$ npm install react-axios
Criamos a estrutura dos nossos projetos. A partir daqui, quero que você revise meus códigos no meu repo. Boa sorte :)
GET /api/Employee
Parâmetro | Medicamento | Explicação |
---|---|---|
api_key | string | Necessário . Sua chave de API. |
GET /api/Employee/${id}
Parâmetro | Medicamento | Explicação |
---|---|---|
id | string | Necessário . Valor chave do item a ser chamado |
{ "employeeID" : 3 , "employeeName" : " piedhorse " , "occupation" : " C# " , "imageName" : " capture_20231646962.jpeg " , "imageFile" : null , "imageSrc" : " https://localhost:44334/Images/capture_20231646962.jpeg " }