Not Possible to upload images using React, Spring gives a error MultipartException: Current request is not a multipart request

huangapple go评论108阅读模式
英文:

Not Possible to upload images using React, Spring gives a error MultipartException: Current request is not a multipart request

问题

It looks like you're facing an issue where your Spring backend is not recognizing the request as a multipart request when you try to upload files. Here are some potential solutions to your problem:

  1. Check Axios Configuration: Ensure that your Axios configuration includes the Content-Type header with the value 'multipart/form-data' when making the POST request for file uploads. You can set this header using Axios like this:

    const config = {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    };
    
    // Make the POST request with the configuration
    axios.post(ASSETS_GOT_FROM_REST_API, asset, config);
    
  2. Verify Frontend Setup: Double-check that your frontend form correctly sets the enctype attribute to "multipart/form-data". You've already done this, but it's essential to make sure there are no typos or issues with HTML structure.

  3. Spring Configuration: Ensure that your Spring application is properly configured to handle multipart requests. You can do this by adding the @EnableMultipartConfig annotation to your Spring configuration class or main application class. Here's an example:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.MultipartConfigFactory;
    import org.springframework.context.annotation.Bean;
    
    import javax.servlet.MultipartConfigElement;
    
    @SpringBootApplication
    public class YourApplication {
        public static void main(String[] args) {
            SpringApplication.run(YourApplication.class, args);
        }
    
        @Bean
        public MultipartConfigElement multipartConfigElement() {
            MultipartConfigFactory factory = new MultipartConfigFactory();
            // Set your configuration options here if needed
            return factory.createMultipartConfig();
        }
    }
    
  4. Controller Configuration: In your Spring controller, make sure that the method handling the file upload has the correct signature. It should include @RequestParam for each file and use MultipartFile as the parameter type, like you've done in your AssetsController.

  5. Check File Sizes: Ensure that the files you're trying to upload are not too large for your server's configured limits. Spring has default limits for file size, so you might need to adjust them if your files are large. You can do this in your Spring configuration as well.

  6. Error Handling: If the issue persists, consider implementing error handling in your Spring controller to log more detailed error messages. You can capture and log exceptions to get more insights into what might be causing the MultipartException.

  7. Database Configuration: Although it's less likely related to your problem, check your database configuration to ensure it's not causing issues. Your entities and database setup appear correct, but sometimes database constraints or configurations can lead to unexpected issues.

By following these steps, you should be able to troubleshoot and resolve the issue with file uploads in your Spring Boot application.

英文:

I can not solve the issue with MultipartException? I try to create a new Item with adding the imges React. Every time I receive the error in Spring console, that current request is not Multipart request.

I created a form for adding the item details and images of item and added enctype="multipart/form-data" but it shows the Axios error AxiosError {message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, …

but Spring console shows me :

org.springframework.web.multipart.MultipartException: Current request is not a multipart request

On spring I created two enities for Items and Images. And on Item add the images should be added together with item ID. For I can take the images from DB by Item id.

Here are the Entities:

Items Entity :

package com.demo.fijinv.Models;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@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:

package com.demo.fijinv.Models;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;
@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;
}

And the controllers for these entities:

Items Controller :

package com.demo.fijinv.Conteollers;

import com.demo.fijinv.Models.Assets;
import com.demo.fijinv.Models.Image;
import com.demo.fijinv.Repositories.AssetsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;

@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(consumes = {MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
    public void saveAsset(@RequestBody Assets assets, @RequestParam("file1") MultipartFile file1, @RequestParam("file2") MultipartFile file2, @RequestParam("file3") 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;
    }
}

Images Controller :

package com.demo.fijinv.Conteollers;

import com.demo.fijinv.Models.Image;
import com.demo.fijinv.Repositories.ImageRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayInputStream;
import java.util.List;

@CrossOrigin("*")
@RestController //no needed to present anything
@RequestMapping("/api/assets/images")
public class ImageController {
    @Autowired
    public ImageRepository imageRepository;

    @GetMapping
    private List<Image> getAllImages(){
        return imageRepository.findAll();
    }

    @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())));
    }
}

Both controllers are configured for allow the requests from all the services by adding @CrossOrigin("*"), which means that spring should receive the requests.

With React I created a simple page with form in modal where I scan add the item with images :

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 [showImage1, setShowImage1] = useState();
const [showImage2, setShowImage2] = useState();
const [showImage3, setShowImage3] = useState();
const saveAsset = (event) => {
event.preventDefault()
const asset = {
assettype,
assetBrand,
assetModel,
image1,
image2,
image3
}
console.log(asset)
AssetsSetvice.addNewAsset(asset).then((res) => {
console.log(res.data)
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 onClick={(id) => { deleteAsset(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' method='POST' enctype="multipart/form-data">
{/* <form className='row' > */}
<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'
// value={file1}
onChange={(e) => {
if (e.target.value != "") {
setShowImage1(URL.createObjectURL(e.target.files[0]))
setImage1(e.target.files[0])
}
}}
/>
</div>
<div className='col-md-4'>
<label className='form-label'> First Image </label>
<input type='file'
className='form-control'
// value={file2}
onChange={(e) => {
if (e.target.value != "") {
setShowImage2(URL.createObjectURL(e.target.files[0]))
setImage2(e.target.files[0])
}
}}
required
/>
</div>
<div className='col-md-4'>
<label className='form-label'> First Image </label>
<input type='file'
className='form-control'
// value={file3}
onChange={(e) => {
if (e.target.value != "") {
setShowImage3(URL.createObjectURL(e.target.files[0]))
setImage3(e.target.files[0])
}
}}
/>
</div>
</form>
</div>
<div className='row mt-4'>
<div className='col-md-4'>
<img style={{ width: "150px" }} className="rounded mx-auto d-block" src={showImage1} />
</div>
<div className='col-md-4'>
<img style={{ width: "150px" }} className="rounded mx-auto d-block" src={showImage2} />
</div>
<div className='col-md-4'>
<img style={{ width: "150px" }} className="rounded mx-auto d-block" src={showImage3} />
</div>
</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

As you can see that in the form there is encription type - multipart: <form className='row' method='POST' enctype="multipart/form-data"> but any way I receive the error.

I use Axios for receive the data from backend, and I created the service for Items:

import axios from "axios";
const ASSETS_GOT_FROM_REST_API = "http://localhost:8082/api/assets";
const IMAGES_GOT_FROM_REST_API = "http://localhost:8082/api/assets/images";
class AssetsSetvice {
getAllAssets() {
return axios.get(ASSETS_GOT_FROM_REST_API);
}
addNewAsset(asset) {
return axios.post(ASSETS_GOT_FROM_REST_API, asset);
}
deleteAsset(assetId) {
return axios.delete(ASSETS_GOT_FROM_REST_API + "/" + assetId);
}
getAllImages() {
return axios.get(IMAGES_GOT_FROM_REST_API);
}
addNewImages(images) {
return axios.addNewImage(IMAGES_GOT_FROM_REST_API, images);
}
}
export default new AssetsSetvice();

when I try to console log the item, which I try to add, I receive :

assetBrand : "fgdfgerg"
assetModel : "gvcvbdxbt"
assettype : "rterte"
file1 : File {name: 'lenovo-3.jpg', lastModified: 1684826589057, lastModifiedDate: Tue May 24 2023 10:23:09 GMT+0300 (Eastern European Summer Time), webkitRelativePath: '', size: 4437, …}
file2 : File {name: 'm700-1.jfif', lastModified: 1684826441152, lastModifiedDate: Tue May 24 2023 10:20:41 GMT+0300 (Eastern European Summer Time), webkitRelativePath: '', size: 3221, …}
file3 : File {name: 'm700-1.jfif', lastModified: 1684826441152, lastModifiedDate: Tue May 23 2023 10:20:41 GMT+0300 (Eastern European Summer Time), webkitRelativePath: '', size: 3221, …}

seams that I am able to send the post request to server, but server refuse it.
And I tried by the different ways.

What I do wrong? How can I solve tis issue?
Can it be related with ManyToOne or OneToMany which were configured in Entity calsses? Because I dont see any other problems.... seams that something blocking in backend side...

答案1

得分: 0

我认为问题出在你的控制器上:

@RequestBody 表示解析 JSON 数据为映射或 Java Bean,并仅支持内容类型为 "application/json;charset=UTF-8"。

也许你可以尝试使用 @ModelAttribute

@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Object> saveAsset(@ModelAttribute Assets assets,
                                        @RequestParam MultipartFile file1,
                                        @RequestParam MultipartFile file2,
                                        @RequestParam MultipartFile file3) throws IOException {
英文:

I think the problem is in your controller:

@RequestBody means to parse JSON data into map or java beans and only support content type is "application/json;charset=UTF-8"

Maybe you can try with @ModelAttribute:

@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity&lt;Object&gt; saveAsset(@ModelAttribute Assets assets,
@RequestParam MultipartFile file1,
@RequestParam MultipartFile file2,
@RequestParam MultipartFile file3)  throws IOException {

huangapple
  • 本文由 发表于 2023年5月24日 17:42:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76322145.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定