Я обсуждал, как загружать изображения в Reactjs с помощью Asp.Net Core WebAPI. Мы создаем веб-API Asp.Net Core и создали базу данных SQL-сервера с ядром платформы сущностей. а затем я создал контроллер API asp.net Core для загрузки изображений.
Мы создали клиентское приложение на Reactjs. Для этого создана форма с загрузкой изображений. Предварительный просмотр выбранного изображения отображается отдельно. Внутри события отправки формы я загрузил выбранное изображение в веб-API Asp.Net.
Инструменты: VS Code, Visual Studio, SSMS, Postman.
Клиент: Reactjs
API: основной веб-API Asp.Net
Этот контроллер подключен к соединению с базой данных и предоставляет методы для перечисления, вставки, обновления и удаления записей сотрудников путем обработки запросов HTTP GET, POST, PUT и DELETE.
Этот контроллер работает путем взаимодействия с формой сотрудника, куда сотрудники могут загружать свои изображения. Эта форма разработана на стороне React и создается с помощью команды npx create-react-app в корневом каталоге проекта.
Соответствующие методы предусмотрены для операций GET, POST, PUT и DELETE. При запросе GET отображаются все существующие записи сотрудников. Запрос PUT обновляет указанную запись о сотруднике, а также используется для изменения изображения профиля сотрудника. Запрос POST добавляет новую запись о сотруднике и загружает изображение профиля сотрудника. Запрос DELETE удаляет указанную запись о сотруднике.
Кроме того, предусмотрены два специальных метода SaveImage() и DeleteImage() для загрузки и удаления изображений в записях сотрудников. Эти методы обрабатывают загрузку и удаление изображений профилей сотрудников.
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 ) ; } } }
Форма позволяет пользователю добавлять или редактировать сотрудника. Компонент предоставляет форму, в которую можно загрузить изображение и ввести информацию об имени и должности сотрудника. Форма состоит из области загрузки изображения и двух полей для ввода текста.
Используется множество переменных. Переменная defaultImageSrc содержит путь к изображению по умолчанию. Переменная InitialFieldValues содержит начальные значения, которые будут использоваться в начальном состоянии компонента. С помощью useState состояния значений отслеживаются внутри компонента.
Функция useEffect используется во время загрузки компонента, и при изменении переменной RecordForEdit она вызывает перезагрузку компонента, а переменная значений обновляется с помощью функции setValues.
Функция handleInputChange вызывается при изменении полей ввода текста. Эта функция фиксирует имя и значение измененного поля с помощью e.target.name и e.target.value и обновляет переменную значений с помощью функции setValues.
Функция showPreview вызывается, если изменилась область загрузки изображения. Эта функция сохраняет путь к выбранному файлу в переменной imageFile, создает предварительный просмотр файла с помощью объекта FileReader и обновляет переменную imageSrc.
Функция validate проверяет правильность формы. Эта функция используется для обеспечения правильного заполнения переменной значений. Функция setErrors обновляет переменную ошибок, а функция resetForm сбрасывает форму и очищает переменную ошибок.
Функция handleFormSubmit вызывается при отправке формы. Эта функция получает данные формы с помощью объекта FormData и отправляет данные на сервер, вызывая функцию addOrEdit.
Функция applyErrorClass применяет класс недопустимого поля к ошибочным полям, что приводит к отображению этих полей с ошибками.
Компонент предоставляет форму, в которую можно загрузить изображение и ввести информацию об имени и должности сотрудника. Форма состоит из области загрузки изображения и двух полей для ввода текста.
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 >
>
)
}
Этот компонент React обрабатывает создание, редактирование, удаление и просмотр записи сотрудника.
Он использует хуки useState и useEffect React. useState определяет переменную состояния для состояния компонента и возвращает setState. useEffect запускает функцию в ответ на такие события, как загрузка или перерисовка компонента.
Axios используется для вызовов веб-служб RESTful. Вспомогательная функция под названием «employeeAPI» создает экземпляры вызовов RESTful и может изменять URL-адрес ресурса, используя функции Axios.
Функция обновитьEmployeeList извлекает записи о сотрудниках с сервера и устанавливает их как состояние компонента с помощью setEmployeeList.
Функция addOrEdit обрабатывает создание или обновление записи сотрудника. Объект FormData содержит записи пользователя для записи сотрудника.
Функция onDelete запрашивает у пользователя подтверждение, хочет ли он удалить запись, а затем отправляет запрос DELETE на сервер.
Функция imageCard используется для отображения карточек сотрудников. На карточке содержится фотография сотрудника, имя, описание должности и кнопка удаления.
Наконец, компонент включает в себя заголовок jumbotron, форму сотрудника и таблицу карточек сотрудников. В таблице отображаются карточки сотрудников с тремя столбцами и дополнительными строками, где третий столбец не занят.
{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 >
)
}
В своем проекте я использую .Net Core 6.0. Пакеты Nuget, которые необходимо загрузить в проекте, перечислены ниже.
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
Прежде всего, давайте добавим класс в нашу папку «Модели». Я назвал его «Модель Сотрудника».
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 ; }
}
Давайте создадим класс Context в нашей папке 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 ; }
}
}
Перейдем в папку Startup.cs и создадим строку подключения для метода ConfigurationServices.
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" ) ) ) ;
}
Перейдем к файлу appsettings.json. Здесь мы записываем информацию о подключении в файл json следующим образом.
"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;"
}
}
После этого мы собираем наш проект, заходим в менеджер пакетов и выполняем операции миграции.
PM > Add-Migration " InitialCreate "
Давайте создадим миграцию с помощью команды и создадим наш проект. Затем дайте эту команду.
PM > update-database
Тогда давайте проверим, создана база данных или нет из MSSQL.
А затем давайте создадим контроллер для нашей модели. Я буду использовать API-контроллер с действиями, использующими контроллер EF.
Сюда придут фотографии, их 2
Мы создали наши элементы управления, указали нашу модель и файл контекста, а также указали имя нашего контроллера.
5 Эти службы называются AddDb.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DevConnection"))); Функция будет создавать запрос для этого контроллера сотрудников каждый раз, когда мы делаем запрос к этому контроллеру сотрудников. Конструктором EmployeeController является общедоступный EmployeeController(EmployeeDbContext context). Значение WorkshopDbContext в этом коде — это метод внедрения зависимостей asp.net, который автоматически управляется самой платформой, поэтому запрос в этом контроллере на данный момент будет взаимодействовать с базой данных.
Теперь давайте создадим клиентскую часть так, чтобы она реагировала на форму сотрудника для загрузки изображений профиля сотрудника. Давайте создадим приложение реагирования в каталоге проекта. Нам нужно открыть командную строку из каталога этого проекта.
npx create-react-app employee-register-client
Команда Oxios для использования на стороне клиента
$ npm install react-axios
Мы создали структуру наших проектов. С этого момента я хочу, чтобы вы просмотрели мои коды в моем репозитории. Удачи :)
GET /api/Employee
Параметр | Лекарство | Объяснение |
---|---|---|
api_key | string | Необходимый . Ваш API-ключ. |
GET /api/Employee/${id}
Параметр | Лекарство | Объяснение |
---|---|---|
id | string | Необходимый . Ключевое значение вызываемого элемента |
{ "employeeID" : 3 , "employeeName" : " piedhorse " , "occupation" : " C# " , "imageName" : " capture_20231646962.jpeg " , "imageFile" : null , "imageSrc" : " https://localhost:44334/Images/capture_20231646962.jpeg " }