Spring MultipartException:使用React上传图片时,当前请求不是multipart请求

vuktfyat  于 2023-05-27  发布在  Spring
关注(0)|答案(1)|浏览(491)

如何使用MultipartException解决问题?我尝试使用imges创建一个新的Item,使用Spring作为后端,React作为前端。
我创建了一个表单,用于添加项目的详细信息和图像,并添加了enctype="multipart/form-data",但它显示Axios错误AxiosError {message: 'Request failed with status code 500', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, …
Spring console显示:

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

在spring上,我为Items和Images创建了两个实体。在项目添加上,图像应与项目ID一起添加。因为我可以从数据库中按项目ID获取图像。
以下是实体:

项目实体:

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

镜像实体:

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;
}

以及这些实体的控制器:

项目控制器:

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;
    }
}

镜像控制器:

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

两个控制器都配置为通过添加@CrossOrigin("*")来允许来自所有服务的请求,这意味着spring应该接收请求。
而对于前端我做了一个简单的页面与形式在模态,我应该能够广告的项目与图像:

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

正如你可以看到,在形式有encription类型- multipart:<form className='row' method='POST' enctype="multipart/form-data">但无论如何我都收到错误。
我使用Axios从后端接收数据,并为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();

当我尝试控制台记录我尝试添加的项目时,我收到:

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

我可以向服务器发送帖子请求,但服务器拒绝了。
我做错了什么?我该如何解决这个问题?
提前感谢!

zz2j4svz

zz2j4svz1#

在ItemsController类中,变量的名称是file 1、file 2和file 3,因此您的axios请求变量的名称必须相同,而不是当前的image 1、image 2、image 3。试试看。你的后端代码部分:
public void saveAsset(@RequestBody Assets assets,@RequestParam(“file1”)MultipartFile file1,@RequestParam(“file2”)MultipartFile file2,@RequestParam(“file3”)MultipartFile file3)

相关问题