文档章节

CKEditor 5 + SpringBoot实战(四):SpringBoot 实现文件上传

树下魅狐
 树下魅狐
发布于 07/07 20:19
字数 2381
阅读 70
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

在本系列的文章中,我将介绍如何在Spring Boot Application中使用CKEditor编辑器。介绍的内容包括基本环境的搭建,文件上传,SpringData JPA数据持久化,CKEditor5的安装,CKEditor图片上传,CKEditor插入视频,获取/设置CKEditor内容等。

在本章节中,主要介绍的内容是在SpringBoot中实现图片上传功能,其中包括上传路径的设置,图片路径映射和上传业务代码的编写。

文件上传

所谓的文件上传就是将客户端的资源通过网络传输到服务端,其本质就是IO流操作。服务端通过IO流读取客户端数据,然后对数据进行解析,获取目标文件数据后,将数据存储到服务端磁盘中。

引入依赖

要实现文件上传,首先需要将所需要的依赖包导入到项目中。这里我们仅导入commons-fileupload和commons-io依赖包。通常,commons-fileupload依赖需要和commons-io一起搭配使用,其中封装了大量的用于操作文件上传的功能,可以帮助我们简化文件上传代码的编写。打开pom.xml配置文件,并加入如下的配置:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

存储路径

上面我们提到,文件上传的本质就是通过IO流将客户端数据存储到服务端的磁盘上。因此,我们需要在服务端的磁盘上规划一个存储空间,用于存储客户端上传的文件。这里我将客户端上传的文件存储到当前项目的类路径下的images文件夹中。

首先,在com.ramostear.ckeditor.common包中创建一个Consts类,然后通过ClassPathResource类获得当前项目的类路径。代码如下:

package com.ramostear.ckeditor.common;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.IOException;

public class Consts {

    public static String FILE_STORAGE_ROOT = getRootPath();

    private static String getRootPath(){
        try {
            Resource resource = new ClassPathResource("");
            return resource.getFile().getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

在代码中,获取到的类路径赋值给了公有静态常量 FILE_STORAGE_ROOT,该常量将在图片路径映射和文件上传业务代码中被使用。

上传配置

一般情况下,客户端的数据可以通过Form表单传递到服务端,但如果是图片等大文件数据,服务端将无法直接解析这些数据(如果是图片文件,也可以将图片通过Base64转码后,将获得的二进制文本传递到服务端),因此,我们需要配置一个专门用于解析客户端上传文件的解析器,另外,如果上传的是图片文件,通常需要在上传成功后显示这些图片,在SpringBoot中,客户端是无法直接去加载这些文件的,这就要求我们还得手动去映射图片地址。

下面,将介绍如何在SpringBoot中配置文件上传的相关参数。最简单的配置方式是配置类继承 WebMvcConfigurationSupport类,然后重写其中对应的方法。首先是文件解析器,这里我们实例化一个CommonsMultipartResolver来作为上传文件的解析器,然后设置其允许上传文件的大小和文件的字符编码,代码如下:

@Bean
public CommonsMultipartResolver multipartResolver(){
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setMaxUploadSize(600000000);
    resolver.setDefaultEncoding("UTF-8");
    return resolver;
}

提示:

在代码中,setMaxUploadSize()方法用于限制上传文件大小的最大值,单位为字节,如果你不想对上传文件做大小限制,可将值设置为-1,默认值也是-1。

最后,为例能够在客户端浏览器中显示上传的图片,我们还需要覆盖WebMvcConfigurationSupport类中的addResourceHandlers()方法,然后对我们定义的图片上传路径做映射。代码如下:

@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**")
        .addResourceLocations("classpath:/static/")
        .addResourceLocations("file:"+ Consts.FILE_STORAGE_ROOT+"/images");
    super.addResourceHandlers(registry);
}

在上述代码中,将”classpath:/static/“和文件上传路径下的图片地址映射为”/**”,例如,上传到服务器的图片存储路径为”C:/ckeditor5-springboot/images/demo.png”,那么该图片的在客户端浏览器中的请求地址为:”http://localhost:8080/demo.png"。完整的配置类如下:

package com.ramostear.ckeditor.config;

import com.ramostear.ckeditor.common.Consts;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class MvcConfiguration extends WebMvcConfigurationSupport {

    @Bean
    public CommonsMultipartResolver multipartResolver(){
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setMaxUploadSize(600000000);
        resolver.setDefaultEncoding("UTF-8");
        return resolver;
    }

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("file:"+ Consts.FILE_STORAGE_ROOT+"/images/");
        super.addResourceHandlers(registry);
    }
}

提示:

别忘记添加配置类的@Configuration注解以及配置方法上的@Bean注解。

实现文件上传

以上环节都准备好后,我们将要实现服务端获取客户端上传文件,并保存到磁盘中的功能。首先,定义一个FileManager接口类,并提供文件上传和删除文件方法,代码如下:

package com.ramostear.ckeditor.service;

import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.util.Collection;

public interface FileManager {

    String upload(CommonsMultipartFile multipartFile);

    boolean remove(String url);

    void remove(Collection<String> urls);
}

upload()方法用于将客户端上传的文件写入到指定的上传目录中,remove()方法在删除内容时被调用。接下来,在FileManagerImpl.java类中实现上述三个方法。

upload

对于文件上传,首先得获取文件存储的路径,然后将上传文件重命名,最后将上传文件数据写入到目标文件中,文件的读写我们将使用CommonsMultipartFile提供的transferTo()方法来实现,代码如下:

@Override
public String upload(CommonsMultipartFile multipartFile) {
    String storageRoot = Consts.FILE_STORAGE_ROOT+ File.separator+"images";
    String path = "";
    String suffix = Objects.requireNonNull(multipartFile.getOriginalFilename())
        .substring(multipartFile.getOriginalFilename().lastIndexOf("."));
    String fileName = SIMPLE_DATE_FORMAT.format(new Date())+"-"
        + UUID.randomUUID().toString().replaceAll("-","").toLowerCase()
        + suffix;
    File file = new File(storageRoot+File.separator+fileName);
    if(!file.getParentFile().exists()){
        file.getParentFile().mkdirs();
    }
    try {
        multipartFile.transferTo(file);
        path = "/"+fileName;
    }catch (IOException e){
        e.printStackTrace();
    }
    return path;
}

文件上传成功后,我们需要将文件的相对地址返回到上一层。

remove

删除文件的方法比较简单,在上传目录中查找传入的文件,如果找到,则删除该文件,反之则不做任何操作。代码如下:

@Override
public boolean remove(String url) {
    String path = Consts.FILE_STORAGE_ROOT+ File.separator+"images"+url;
    File file = new File(path);
    if(file.exists() && file.isFile()){
        return file.delete();
    }
    return false;
}

FileManagerImpl.java类的完整代码如下:

package com.ramostear.ckeditor.service.impl;

import com.ramostear.ckeditor.common.Consts;
import com.ramostear.ckeditor.service.FileManager;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

@Service(value = "fileManager")
public class FileManagerImpl implements FileManager {
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");

    @Override
    public String upload(CommonsMultipartFile multipartFile) {
        String storageRoot = Consts.FILE_STORAGE_ROOT+ File.separator+"images";
        String path = "";
        String suffix = Objects.requireNonNull(multipartFile.getOriginalFilename())
                .substring(multipartFile.getOriginalFilename().lastIndexOf("."));
        String fileName = SIMPLE_DATE_FORMAT.format(new Date())+"-"
                + UUID.randomUUID().toString().replaceAll("-","").toLowerCase()
                + suffix;
        File file = new File(storageRoot+File.separator+fileName);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        try {
            multipartFile.transferTo(file);
            path = "/"+fileName;
        }catch (IOException e){
            e.printStackTrace();
        }
        return path;
    }

    @Override
    public boolean remove(String url) {
        String path = Consts.FILE_STORAGE_ROOT+ File.separator+"images"+url;
        File file = new File(path);
        if(file.exists() && file.isFile()){
            return file.delete();
        }
        return false;
    }

    @Override
    public void remove(Collection<String> urls) {
        if(!CollectionUtils.isEmpty(urls)){
            urls.stream().allMatch(this::remove);
        }
    }
}

提示:

别忘记在实现类上添加@Service注解。

上传控制器

最后,我们通过Spring MVC来实现图片上传控制器。根据查阅官网相关API可知,CKEditor是通过POST请求的方式将名为“upload”的文件提交到后台,后台将数据处理完成后,需要返回如下格式的数据:

{
    "uploaded":true,
    "filename":"filename",
    "url":'url'
}

当图片上传成功后,uploaded的值为true,反之为false。

下面是上传图片控制器的详细代码:

package com.ramostear.ckeditor.controller;

import com.alibaba.fastjson.JSONObject;
import com.ramostear.ckeditor.service.FileManager;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/upload")
public class FileUploadController {

    private final FileManager fileManager;

    FileUploadController(FileManager fileManager){
        this.fileManager = fileManager;
    }

    @PostMapping("/image")
    public JSONObject image(@RequestParam(name = "upload")CommonsMultipartFile file){
        JSONObject json = new JSONObject();
        if(file == null || file.isEmpty()){
            json.put("uploaded",false);
            json.put("url","");
            return json;
        }
        if(StringUtils.isBlank(file.getOriginalFilename()) || !isAllow(file.getOriginalFilename())){
            json.put("uploaded",false);
            json.put("url","");
            return json;
        }
        String url = fileManager.upload(file);
        if(StringUtils.isBlank(url)){
            json.put("uploaded",false);
            json.put("url","");
            return json;
        }else{
            json.put("uploaded",true);
            json.put("url",url);
            return json;
        }
    }

    private boolean isAllow(String fileName){
        String[] allowFiles = {".gif",".png",".jpg",".jpeg",".bpm",".svg"};
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        List<String> suffixList = Arrays.stream(allowFiles).collect(Collectors.toList());
        return suffixList.contains(suffix);
    }
}

其中,isAllow()方法用于限定上传的文件只能是图片类型,在FileUploadController类中,使用基于构造方法注入的方式将FileManager实例化对象注入到其中,并完成图片上传操作。另外,在该类中使用到了StringUtils工具类和Alibaba FastJson类,因此,你还需要向pom.xml文件中添加如下的依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.57</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

上传测试

由于目前还未搭建前端页面,因此使用浏览器来测试文件上传功能,但我们可以借助Postman来测试文件上传功能。打开Postman软件,然后新建一个请求,请求方式为POST,接着在地址栏输入“http://localhost:8080/upload/image ” ;最后,在Body选项栏下,数据提交格式为form-data,KEY的值为“upload”,类型为”file”,VALUE为待上传的图片。配置完成后点击”Send”按钮进行测试。测试流程如下图:

图片上传成功后,我们可以在项目的target/classes/images/目录下查看到刚刚上传的图片“20200707-3edd0218d4194f4f803e710573f07390.png”,如下图:

除此之外,我们开可以在浏览器中输入“ http://localhost:8080/20200707-3edd0218d4194f4f803e710573f07390.png ”来访问刚才上传的图片,如下图:

本章小结

在本章内容中,详细介绍了使用SpringBoot实现文件上传的详细过程,并使用Postman工具对文件上传功能做了测试。在下一章中,将介绍SpringBoot和Freemaker模板引擎的整合。

未经作者允许,请勿擅自转载,转载请注明文章作者和出处。

树下魅狐

树下魅狐

粉丝 19
博文 14
码字总数 35391
作品 5
贵阳
技术主管
私信 提问
加载中
请先登录后再评论。
用vertx实现高吞吐量的站点计数器

工具:vertx,redis,mongodb,log4j 源代码地址:https://github.com/jianglibo/visitrank 先看架构图: 如果你不熟悉vertx,请先google一下。我这里将vertx当作一个容器,上面所有的圆圈要...

jianglibo
2014/04/03
3.9K
3
SQLServer实现split分割字符串到列

网上已有人实现sqlserver的split函数可将字符串分割成行,但是我们习惯了split返回数组或者列表,因此这里对其做一些改动,最终实现也许不尽如意,但是也能解决一些问题。 先贴上某大牛写的s...

cwalet
2014/05/21
9.6K
0
CDH5: 使用parcels配置lzo

一、Parcel 部署步骤 1 下载: 首先需要下载 Parcel。下载完成后,Parcel 将驻留在 Cloudera Manager 主机的本地目录中。 2 分配: Parcel 下载后,将分配到群集中的所有主机上并解压缩。 3 激...

cloud-coder
2014/07/01
6.8K
1
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.3K
16
代码生成器--Codgen

Codgen是一个基于数据库元数据模型,使用freemarker模板引擎来构建输出的代码生成器。freemarker的数据模型结构通常来说都是一个Map树状结构模型,codgen也不例外,它的数据模型这棵树的根节...

黄天政
2013/01/29
1.4W
2

没有更多内容

加载失败,请刷新页面

加载更多

好用到爆的 Java 技巧

本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写法问题,如果读者自认为基础问题和写法问题都是不是问题,那请忽略这篇文章,节省出时间去做一些有意义的事情...

码农突围
43分钟前
8
0
消息队列(MessageQueue)-分析

这里分析消息队列的原理和一般做法和其理念价值 这里还会 分析 NATS 和其可改进点 TODO

梦想游戏人
47分钟前
20
0
Redis 教程

Redis 教程 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的...

rootliu
49分钟前
9
0
SPSSAU 付费数据研究报告服务

SPSSAU-付费数据分析报告服务(周老师提供) 本文分享自微信公众号 - SPSSAU(spssau)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起...

SPSSAU
2017/11/08
0
0
芋艿-springcloud gateway

http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github springcloud gateway 官方文档 https://cloud.spring.io/spring-cloud-gateway/reference/html/#gatewayfilter-factories......

Java搬砖工程师
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部