Загрузить изображения вместе с сущностью в React и SpringBoot
Как загрузить изображения вмести с сущностью, используя React как фронт и Spring как Бэкенд.
Мне нужно загрузить детали элемента с изображениями. Я могу добавить элемент, но не могу добавить изображения.
У меня есть две разные сущности:
Item:
@Entity
@Table(name = "assets")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Assets {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
@Column(name = "asset_type")
private String assettype;
@Column(name = "asset_brand")
private String assetBrand;
@Column(name = "asset_model")
private String assetModel;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "assets")
private List<Image> images = new ArrayList<>();
private Long previewImageId;
private LocalDateTime creationDate;
@PrePersist //to read about inversion of control
private void init() {
creationDate = LocalDateTime.now();
}
public void addImageToItemName(Image image) {
image.setAssets(this);
images.add(image);
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Items> invItem = new ArrayList<>();
}
И Images:
@Entity
@Table(name = "images")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "originalFileName")
private String originalFileName;
@Column(name = "size")
private Long size;
@Column(name = "contentType")
private String contentType;
@Column(name = "isPreviewImage")
private boolean isPreviewImage;
@Column(length = 10000000)
@Lob
private byte[] bytes;
@ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
private Assets assets;
@ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
private List<Items> items;
}
И эти изображения должны быть связанны с ID элементов, что бы в дальнейшем я выводил детали элементов с изображениями.
Контроллер эелементов у меня:
@CrossOrigin("*")
@RestController
@RequestMapping("/api/assets")
public class AssetsController {
@Autowired
public AssetsRepository assetsRepository;
@GetMapping
public List<Assets> getAllAssets(){
return assetsRepository.findAll();
}
@DeleteMapping("{id}")
public ResponseEntity<Assets> deleteAsset(@PathVariable Long id){
assetsRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.OK);
}
@PostMapping
public void saveAsset(@RequestBody Assets assets, MultipartFile file1, MultipartFile file2, MultipartFile file3) throws IOException {
Image image1;
Image image2;
Image image3;
if(file1.getSize() != 0){
image1 = toImageEntity(file1);
// image1.setPreviewImage(true);
assets.addImageToItemName(image1);
}
if(file2.getSize() != 0){
image2 = toImageEntity(file2);
assets.addImageToItemName(image2);
}
if(file3.getSize() != 0){
image3 = toImageEntity(file3);
assets.addImageToItemName(image3);
}
Assets itemFromDB = assetsRepository.save(assets);
itemFromDB.setPreviewImageId(itemFromDB.getImages().get(0).getId());
assetsRepository.save(assets);
}
private Image toImageEntity(MultipartFile file) throws IOException {
Image image = new Image();
image.setName(file.getName());
image.setOriginalFileName(file.getOriginalFilename());
image.setContentType(file.getContentType());
image.setSize(file.getSize());
image.setBytes(file.getBytes());
return image;
}
}
И так же у меня создан метод toImageEntity что бы добавлять изобрадения как сущности, при добавлении элемента.
И Контроллер изображений ImageController :
@CrossOrigin("*")
@RestController //no needed to present anything
@RequiredArgsConstructor
@RequestMapping("/api/images/")
public class ImageController {
@Autowired
public ImageRepository imageRepository;
@GetMapping("{id}")
private ResponseEntity<?> getImageByID(@PathVariable Long id){
Image image = imageRepository.findById(id).orElse(null);
return ResponseEntity.ok()
.header("filename", image.getOriginalFileName())
.contentType(MediaType.valueOf(image.getContentType()))
.contentLength(image.getSize())
.body(new InputStreamResource(new ByteArrayInputStream(image.getBytes())));
}
}
Для добавления я создал форму с добавлением имени, бренда, модели и трех изображений для актива:
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom';
import { Modal, Button } from 'react-bootstrap'
import AssetsSetvice from "../../Services/AssetsService"
const AssetsComponenet = () => {
const [show, setShow] = useState(false);
const modalShow = () => setShow(true);
const modalHide = () => setShow(false);
// getting all assets
const [assets, setAssets] = useState([]);
useEffect(() => {
AssetsSetvice.getAllAssets().then((res) => {
setAssets(res.data);
}).catch(err => {
console.log(err)
})
})
const [assettype, setAssettype] = useState('');
const [assetBrand, setAssetBrand] = useState('');
const [assetModel, setAssetModel] = useState('');
const [image1, setImage1] = useState('');
const [image2, setImage2] = useState('');
const [image3, setImage3] = useState('');
const saveAsset = (event) => {
event.preventDefault()
const asset = {
assettype,
assetBrand,
assetModel,
image1,
image2,
image3
}
AssetsSetvice.addNewAsset(asset).then((res) => {
event.preventDefault();
}).catch(err => {
console.log(err)
})
}
const saveAndClose = (ev) => {
saveAsset(ev);
setShow(false);
}
const deleteAsset = (id) => {
if (window.confirm("Are you sure want to delete this asset?")) {
AssetsSetvice.deleteAsset(id).then((response) => {
console.log(`Asset with ${id} was deleted`);
// window.location.replace("/users");
}).catch(error => {
console.log(`Something went worng : \n ${error}`);
})
}
}
let count = 1;
return (
<>
<div className='container'>
<h2 className='text-center mt-4 mb-4 bold'> List of Items</h2>
<div className='mb-3'>
<button type='button' className='btn btn-primary' data-toggle="modal" onClick={modalShow} data-target="#addNewItemModal">
Add new Asset
</button>
</div>
<table className='table table-bordered table-striped'>
<thead>
<tr>
<th className="th-sm">№</th>
<th className="th-sm">Name</th>
<th className="th-sm">Brand</th>
<th className="th-sm">Model</th>
<th className="th-sm"> Action </th>
</tr>
</thead>
<tbody>
{
assets.map(
function (item) {
return <tr key={item.id}>
{/* <tr> */}
<th className="th-sm">{count++}</th>
<th className="th-sm">Asset Name</th>
<th className="th-sm">Asset Brand</th>
<th className="th-sm">Asset Model</th>
<th className="th-sm">
{/* <Link to={`/item-delete/${item.id}`} className="btn btn-primary"> Delete Item </Link> */}
<Link to={`/assets/${item.id}`} className="btn btn-primary"> Delete Asset </Link>
</th>
</tr>
}
)
}
</tbody>
</table>
<Modal show={show} size='lg' onHide={modalHide} centered>
<Modal.Header closeButton>
<Modal.Title center>
Add New Item
</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className='container-fluid'>
<form className='row' enctype="multipart/form-data">
<div className='col-md-4'>
<label className='form-label'> Item Name </label>
<input type='text'
placeholder='Item Name'
className='form-control'
// value={itemname}
onChange={(e) => {
if (e.target.value != "") {
setAssettype(e.target.value)
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> Item Brand </label>
<input type='text'
placeholder='Item Brand'
className='form-control'
// value={itembrand}
onChange={(e) => {
if (e.target.value != "") {
setAssetBrand(e.target.value)
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> Item Model </label>
<input type='text'
placeholder='Item Model'
className='form-control'
// value={itemmodel}
onChange={(e) => {
if (e.target.value != "") {
setAssetModel(e.target.value)
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> First Image </label>
<input type='file'
className='form-control'
onChange={(e) => {
if (e.target.value != "") {
setImage1(e.target.value)
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> First Image </label>
<input type='file'
className='form-control'
onChange={(e) => {
if (e.target.value != "") {
setImage2(e.target.value)
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> First Image </label>
<input type='file'
className='form-control'
onChange={(e) => {
if (e.target.value != "") {
setImage3(e.target.value)
}
}}
required
/>
</div>
</form>
</div>
</Modal.Body>
<Modal.Footer center>
<Button center onClick={(e) => { saveAndClose(e) }} variant="primary">Add New Asset</Button>
</Modal.Footer>
</Modal>
</div>
</>
)
}
export default AssetsComponenet
И каждый раз когда я добавляю новый элемент с изображениями, мне выдает ошибку :
AxiosError {message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, …}code: "ERR_BAD_RESPONSE"config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}message: "Request failed with status code 500"name: "AxiosError"request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}response: {data: {…}, status: 500, statusText: '', headers: AxiosHeaders, config: {…}, …}stack: "AxiosError: Request failed with status code 500\n at settle (http://localhost:3000/static/js/bundle.js:76728:12)\n at XMLHttpRequest.onloadend (http://localhost:3000/static/js/bundle.js:75419:66)"[[Prototype]]: Error
Я сделал этот проект на чистом Spring и решил сделать тоже самое на React Но на этом моменте я запнулся.
Подскажите пожалуйста что я делаю не так.
Заранее спасибо!