React-Ant Design, Spring Boot 上传图片

774 阅读2分钟

毕设想要复刻一下超级简历,发现在实现上传图片到后端这个需求时即使已经写了好几次还是会花时间去调试。在Google上也没有我想要的Demo,于是这次调试好后打算整理一下前端React和后端Spring Boot如何去做。

主要思想:

graph TD
前端执行上传操作 --> 后端接收并存储 --> 后端生成URL并返回给前端 -->前端拿到图片的URL并在提交所有信息时提交图片的URL

前端: 前端选择了Ant Design的上传组件 (官方Demo),这是个封装好的上传组件,用户选择好文件之后就会自动向action参数中的URL发起POST请求。我的需求是要求用户上传一张自己的证件照,代码如下:

import {LoadingOutlined, PlusOutlined} from '@ant-design/icons';
import { message, Upload } from 'antd';
import { useState } from 'react';

const getBase64 = (img, callback) => {
    const reader = new FileReader();
    reader.addEventListener('load', () => callback(reader.result));
    reader.readAsDataURL(img);
};

const beforeUpload = (file) => {
    console.log(file)
    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';

    if (!isJpgOrPng) {
        message.error('You can only upload JPG/PNG file!');
    }

    const isLt2M = file.size / 1024 / 1024 < 2;

    if (!isLt2M) {
        message.error('Image must smaller than 2MB!');
    }

    return isJpgOrPng && isLt2M;
};

const PhotoUpload = () => {
    const [loading, setLoading] = useState(false);
    const [imageUrl, setImageUrl] = useState();

    const handleChange = (info) => {
        console.log(info)
        if (info.file.status === 'uploading') {
            setLoading(true);
            return;
        }

        if (info.file.status === 'done') {
            // Get this url from response in real world.
            getBase64(info.file.originFileObj, (url) => {
                setLoading(false);
                setImageUrl(url);
            });
        }
    };

    const uploadButton = (
        <div>
            {loading ? <LoadingOutlined /> : <PlusOutlined />}
            <div
                style={{
                    marginTop: 8,
                }}
            >
                Upload
            </div>
        </div>
    );
    return (
        <Upload
            accept={".jpg"}
            name="photo"
            listType="picture-card"
            className="avatar-uploader"
            showUploadList={false}
            maxCount={1}
            action="http://localhost:8080/api/upload"
            beforeUpload={beforeUpload}
            onChange={handleChange}
        >
            {imageUrl ? (
                <img
                    src={imageUrl}
                    alt="avatar"
                    style={{
                        width: '100%',
                    }}
                />
            ) : (
                uploadButton
            )}
        </Upload>
    );
};

export default PhotoUpload;

其中,比较重要的是Upload组件中传的参数,

  • “name”属性是后端接受的字段名称,因此保持和后端接口一致非常重要。
  • “action”是上传的URL。
  • “beforeUpload”函数是在文件上传前的一个钩子,官方的用法是在此进行文件类型的判断,此函数返回false则会取消上传。如果想要进行custom upload,可以考虑在此拦截组件默认的上传行为。
  • “ handleChange”函数在上传中、完成、失败都会调用这个函数。 我在beforeLoad中打印出了file对象: image.png 前端的使用方式就是这样了,最后可以达到这样的效果: image.png

其余的样式和多个文件上传可以参考官方Demo中的做法。

后端: 之前的项目中只使用Node.js实现过上传接口,现在开始学习Spring Boot后再实现一次。 以下是controller中的借口代码:

package com.resume.resumespringboot.controller;

import com.resume.resumespringboot.utils.JSONResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;

@RestController
@Slf4j
@CrossOrigin(origins = "http://localhost:3000")
@RequestMapping("api")
public class FileController {

    @PostMapping("upload")
    public JSONResult handleFileUpload(@RequestParam("photo") MultipartFile file ) {

        String fileName = file.getOriginalFilename();
        try {
            file.transferTo( new File(new File("src/main/resources/static").getAbsolutePath()+ "/" + fileName));
        } catch (Exception e) {
            return JSONResult.errorMsg(e.getMessage());
        }
        return JSONResult.ok("File uploaded successfully.");
    }
}

值得注意的是接收的字段名是和前端一样的“photo”,类型为MultipartFile。目前我将接收到的文件存在resources下的static中。JSONResult是自己封装的响应类,目前返回给前端的是一个message,理应返回给前端一个URL。 之后还想分享一下使用react-pdf这个库生成用户的定制简历,bye!