前言

最近在实现小图片上传的过程中刚开始我使用base64字符串做为后台接口参数传递给后台并解析保存也没问题,但是发现第2次及之后就报下面的错:

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144

然后问了AI给的回复如下:

这个问题通常是由于 Spring Boot 默认的 DataBuffer 限制导致的,默认限制为 256 KB。当你传递大于这个大小的 Base64 字符串时,就会出现 DataBufferLimitException。

那没办法,所以我只能退一步使用上传File来实现了,以为自己可以减少工作量,唉,乖乖实现吧!

但是有个问题就是我不仅要上传文件,还要携带参数,之前没有这样的需求,如何做呢?

操作

问了AI,下面是实现前端代码,如下所示:

antd代码:

const props2 = {
        // 不显示上传文件列表
        showUploadList: false,
        // name: 'file',
        accept: "image/png, image/jpeg",
        multiple: false,
        maxCount: 1,
        beforeUpload: async (file) => {
            console.log('file=>', file)
            const isLt2M = file.size / 1024 / 1024 < 2;
            if (!isLt2M) {
                message.error('二维码上传文件不能大于2MB!');
            }

            setDlg({
                type: 'saveAndRead',
                title: '保存到我的文件',
                show: true,
                // 使用marked把markdown格式转换成html格式供ckeditor富文本编辑器使用
                data: {
                    name: '',
                    file: file
                },
                onSuccess: async (d) => {
                    console.log('success d=', d)
                    if (d?.text){
                        message.success("解析成功")
                        setParsingQrcode2(d.text)
                    }else{
                        message.error("解析失败")
                    }
                }
            })
            return false;
        },
        onDrop(e) {
            console.log('Dropped files', e.dataTransfer.files);
        },
    };


 <Dragger {...props2}>
    <div className={'flex w-full flex-col'}>
        <p className="ant-upload-drag-icon">
            <UploadOutlined/>
        </p>
        <p className="ant-upload-text">单击或将文件拖动到此区域以上传</p>
        <p className="ant-upload-text">(提示:先上传到我的文件再解析)</p>
    </div>
</Dragger>

请求接口代码:

 try {
        const formData = new FormData();
        formData.append('file', params.file);
        formData.append('categoryId', params.categoryId);
        formData.append('fileName', params.fileName);

        const res = await axios.post(`/space/crud/file/addFileForQrcodeAndRead`, formData);
        return res.data
    } catch (error) {
        return thunkAPI.rejectWithValue({errorMsg: error.message});
    }

后台实现:

@PostMapping("/crud/file/addFileForQrcodeAndRead")
public Mono<ResponseEntity<?>> addFileForQrcodeAndRead(@RequestPart("file") Mono<FilePart> file,
                                                           @RequestPart("categoryId") String cid,
                                                           @RequestPart("fileName") String filename) throws IOException {
        LoginUser loginUser = UserContext.getUser();
        if (loginUser == null) {
            return Mono.just(ResponseEntity.ok(HttpStatus.UNAUTHORIZED));
        }

        if (!StringUtils.hasLength(cid) || !StringUtils.hasLength(filename)) {
            return Mono.just(ResponseEntity.ok(new ResultInfo<>(ResultStatus.DATA_EMPTY)));
        }


        return file.flatMap(fp -> {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            return fp.content().collectList().flatMap(dataBuffers -> {

                try {
                    dataBuffers.forEach(buffer -> {
                        byte[] bytes = new byte[buffer.readableByteCount()];
                        buffer.read(bytes);
                        baos.write(bytes, 0, bytes.length);
                    });
                    byte[] bytes = baos.toByteArray();
                    Long size = (long) bytes.length;
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                    BufferedImage bufferedImage = ImageIO.read(inputStream);

                    BufferedImage bufferedImage2 = addBorder(bufferedImage, 20);

                    LuminanceSource source = new BufferedImageLuminanceSource(bufferedImage2);
                    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

                    Result result = new MultiFormatReader().decode(bitmap);

                    // 注意:重置指针,方便第2次读取,否则下面获取不到inputStream
                    inputStream.reset();
                    // 是否能检测到数据,不能检测数据就添加不成功
                    if (StringUtils.hasLength(result.getText())) {

                    }
                    // 如何二维码解析出来了,但是添加和上传图片失败,那也返回解析结果
                    return Mono.just(ResponseEntity.ok(new ResultSuccess<>(result)));
                } catch (Exception ex) {
                    log.info("uploadFileAndRead ex={}", ex.getMessage());
                    return Mono.just(ResponseEntity.ok(new ResultInfo<>(ResultStatus.Exception)));
                }
            });
        });

这样就完成了上传文件并且携带参数了。

总结

1、Base64做为参数传递到后台会导致参数大小超过限制,因此使用File上传。
2、后台接收的时候要统一使用RequestPart,刚开始我使用了下面的代码就出问题了(AI给的代码):

public ResponseEntity<?> uploadFileAndRead(
@RequestPart("upload") MultipartFile file,
@RequestParam("additionalParam1") String param1,
@RequestParam("additionalParam2") String param2)

3、文件流inputStream读取之后再次使用就会拿不到,解决办法:

inputStream.reset();

AI的回答是这样的:

ByteArrayInputStream 的数据流被读取一次后,无法再次读取,因为 ByteArrayInputStream 的内部指针已经到达了流的末尾。

要解决这个问题,可以在读取流数据后重置 ByteArrayInputStream,或者在需要重新使用时重新创建 ByteArrayInputStream。

引用


Awbeci
3.1k 声望213 粉丝

Awbeci