超详细!利用SpringBoot+SpringCloud做一个问答项目(十二)

目录

一、文件上传

1.使用Ajax实现文件上传 

2.处理多文件上传

二、自定义summernote上传

1.定义浏览图片后的回调

2.在服务端接收客户端上传的图片

3.整合页面实现文件上传

4.使用资源项目对外提供图片访问

5.服务器端向客户端响应上传的文件的URL

6.在网页中显示新上传的文件

7.添加测试数据

一、文件上传


1.使用Ajax实现文件上传 

首先,需要在页面中引入jQuery框架,先准备一个jQuery.js文件,放到静态资源目录下,然后,在HTML文件中引用:

为了避免点击按钮时提交同步请求,先将按钮的类型改为button,然后绑定单击事件:

此时,启动项目,点击按钮时,即可弹出警告!

测试无误后,在以上函数中实现异步提交:

2.处理多文件上传

在处理多文件上传时,首先,必须明确这多个文件的数量、定位是否明确!

假设需要实现的功能是“上传身份证照片的正反面照片”,则需要上传2张图片,且图片的数量及每一张图片的定位(作用)是明确的,则可以在页面中设计2个上传控件,这2个控件使用不同的名字,例如:

<form id="form-upload">
    <p>请选择身份证的正面照片(个人信息面):</p>
    <p><input type="file" name="file1"></p>
    <p>请选择身份证的反面照片(国徽面):</p>
    <p><input type="file" name="file2"></p>
    <p><input id="btn-submit" type="button" value="上传"></p>
</form>

然后,在控制器中,声明2个MultipartFile类型的参数用于接收这2张图片对应的对象:

@PostMapping("/upload")
public String upload(MultipartFile file1, MultipartFile file2) {
	// 分别处理file1和file2
}

假设需要上传的文件的数量是不确定的,各文件的定位(作用)是相同的,例如发表一篇微博,或者管理网络上的相册等,在设计页面时,可以在<input type="file" />控件上添加multiple属性,在用户在浏览需要上传的文件时,可以按住键盘的Ctrl键选择多个文件:

<form id="form-upload">
    <p>请选择选择您要上传的文件(按住Ctrl键可以选择多个):</p>
    <p><input type="file" name="files" multiple="multiple"></p>
    <p><input id="btn-submit" type="button" value="上传"></p>
</form>

然后,在控制器中,使用MultipartFile数组来接收客户端提交的多个文件并处理即可:

@PostMapping("/upload")
public String upload(MultipartFile[] files) {
	// 可以使用循环语法对参数files进行处理
}

二、自定义summernote上传


1.定义浏览图片后的回调

使用summernote上传文件时,当浏览图片并确定,默认情况下,就会将图片转换为Base64编码的字符串,并创建Image对象插入到summernote编辑区,同时,summernote允许自定义浏览图片并确定后的回调!

具体操作步骤如下:在create.html的最下方存在初始化summernote的代码:

应该将以上代码(除了最下方的$('select')那一行以外)使用专门的文件来保存,所以,在static/js/commons下创建init_summernote.js文件,将以上代码剪切到该文件中:

并在create.html中引用该文件:

接下来,在init_summernote.js中自定义回调:

再次运行项目,尝试上传图片,当选中图片后,会提示警告框,并且,选中的图片也不会被转换为Base64编码并插入到编辑区了:

2.在服务端接收客户端上传的图片

可以在api-question中实现文件上传,先在StrawApiQuestionApplicationStrawGatewayApplication中自定义允许上传的文件的大小的上限值(因为SpringBoot默认已经设置了上限文件大小的限制):

@Bean
public MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    factory.setMaxFileSize(DataSize.ofMegabytes(500));
    factory.setMaxRequestSize(DataSize.ofMegabytes(500));
    return factory.createMultipartConfig();
}

关于以上配置,也可以在application.properties中添加配置来实现!

在处理上传的过程中,还会进行相关的判断,如果上传的图片不符合规则,应该抛出对应的异常!则在straw-commonscn.tedu.straw.commons.ex包下创建异常类:

// 自定义的“上传文件异常”的基类异常
public class FileUploadException extends RuntimeException {}
// 上传的文件为空,父类是自定义的异常(注意:另有一个异常与自定义的FileUploadException同名)
public class FileEmptyException extends FileUploadException {}

public class FileSizeException extends FileUploadException {}

public class FileTypeException extends FileUploadException {}

public class FileUploadIOException extends FileUploadException {}

public class FileStateException extends FileUploadException {}

由于创建了新的异常类(1个基数 + 5个子类),需要在R.State中添加5个对应的常量:

/**
 * 错误:上传的文件为空
 */
Integer ERR_FILE_EMPTY = 4006;
/**
 * 错误:上传的文件大小超出了限制
 */
Integer ERR_FILE_SIZE = 4007;
/**
 * 错误:上传的文件类型超出了限制
 */
Integer ERR_FILE_TYPE = 4008;
/**
 * 错误:上传文件时出现读写错误 
 */
Integer ERR_FILE_IO = 4009;
/**
 * 错误:上传文件的状态错误
 */
Integer ERR_FILE_STATE = 4010;

同时,在straw-api-questionGlobalExceptionHandler类中处理以上5种异常:

关于检查上传时的限制(例如文件大小的上限、文件的类型)可以定义在配置文件中,便于统一管理!可以在straw-api-questionapplication.properties中添加自定义配置,同时,还可以添加相关的配置信息,例如将上传的文件保存到哪里:

# 自定义配置:上传文件-问题的图片-文件大小限制值
project.fileupload.question-image.max-size=5242880
# 自定义配置:上传文件-问题的图片-文件类型限制值
project.fileupload.question-image.content-types=image/jpeg, image/png, image/gif
# 自定义配置:上传文件-根目录
project.fileupload.base-dir=E:/static-resource

需要读取以上配置值时,可以通过@Value注解来读取:

@Value("${project.fileupload.question-image.max-size}")
long maxSize;
@Value("${project.fileupload.question-image.content-types}")
List<String> contentTypes;
@Value("${project.fileupload.base-dir}")
String baseDir;

straw-api-questionQuestionController中添加处理请求的方法,处理上传的请求:

@PostMapping("/image/upload")
public R imageUpload(MultipartFile file) {
    // 检查上传的图片是否为空
    if (file.isEmpty()) {
        throw new FileEmptyException("上传失败!请选择有效的图片文件!");
    }

    // 检查上传的图片大小是否超标
    if (file.getSize() > maxSize) {
        throw new FileSizeException("上传失败!不允许上传超过" + maxSize / 1024 / 1024 + "MB的图片文件!");
    }

    // 检查上传的图片类型是否超标
    if (!contentTypes.contains(file.getContentType())) {
        throw new FileTypeException("上传失败!图片类型错误!允许上传的文件类型有:" + contentTypes);
    }

    // 确定上传的文件保存到哪个文件夹
    // 利用配置文件中的配置值,结合年月得到例如 E:/static-resource/2020/09 这样的文件夹
    // 2020/09 < DateTimeFormatter.of("yyyy/MM").format(LocalDate.now())
    // 并结合File对象的exists()判断文件夹是否存在,如果不存在,则通过mkdirs()创建
    String yearAndMonth = DateTimeFormatter.ofPattern("yyyy/MM").format(LocalDate.now());
    File parent = new File(baseDir, yearAndMonth);
    if (!parent.exists()) {
        // parent.mkdir();
        parent.mkdirs();
    }

    // 确定上传的文件使用什么文件名
    String filename = System.currentTimeMillis() + "-" + System.nanoTime();
    // 确定上传的文件使用什么扩展名
    String originalFilename = file.getOriginalFilename();
    int beginIndex = originalFilename.lastIndexOf(".");
    String suffix = originalFilename.substring(beginIndex);
    // 确定上传的文件使用什么文件全名
    String child = filename + suffix;

    // 创建上传的文件在保存时使用的File对象
    File dest = new File(parent, child);
    try {
        // 执行保存上传的文件
        file.transferTo(dest);
    } catch (IOException e) {
        // 抛出FileUploadIOException
        throw new FileUploadIOException("上传失败!上传过程中出现读写错误,请稍后再次尝试!");
    } catch (IllegalStateException e) {
        // 抛出FileStateException
        throw new FileStateException("上传失败!文件状态有误,请检查上传的文件是否有效!");
    }

    // 日志
    log.debug("上传成功!文件路径:{}", dest);

    // 返回,暂定为R.ok()
    return R.ok();
}

3.整合页面实现文件上传

init_summernote.js中处理选择文件后的回调函数:

$(document).ready(function () {
    $('#summernote').summernote({
        height: 300,
        tabsize: 2,
        lang: 'zh-CN',
        placeholder: '请输入问题的详细描述...',
        callbacks: {
            onImageUpload: function (files) {
                // alert('将执行自定义的上传图片回调!文件数量=' + files.length);
                if (files.length > 1) {
                    alert("一次只能选择1张图片!");
                    return;
                }

                let file = files[0];
                let data = new FormData();
                data.append('file', file);

                $.ajax({
                    url: '/api-question/v1/questions/image/upload',
                    data: data,
                    contentType: false,
                    processData: false,
                    type: 'POST',
                    success: function (json) {
                        if (json.state == 2000) {
                            alert('上传成功!');
                        } else {
                            alert(json.message);
                        }
                    }
                });
            }
        }
    });
});

完成后,依次重启straw-api-questionstraw-gateway项目,在summernote中浏览文件并确定,将会上传选中的文件,并且,在E:/static-resource文件下来可以看到创建的子级文件夹及文件。

4.使用资源项目对外提供图片访问

创建完成后,调整pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>straw</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>straw-resource</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>straw-resource</name>

    <dependencies>
        <!-- Eureka Client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- web:允许项目启动在Tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

然后,删除项目中的test文件夹!

然后,在application.properties中添加必要的配置:

# 显式的配置当前项目部署到服务器时运行在哪个端口号
server.port=8888

# 应用程序名称
spring.application.name=resource

# 静态资源目录
spring.resources.static-locations=file:E:/static-resource

# 是否使用IP地址来注册,如果是,则注册IP地址,如果否,则注册主机名
eureka.instance.prefer-ip-address=true
# 当前项目在Eureka Server中注册的主机名,仅当prefer-ip-address设置为false时有效
eureka.instance.hostname=localhost
# 当前项目在Eureka Server中注册的IP地址,当prefer-ip-address设置为true时有效
eureka.instance.ip-address=127.0.0.1
# 当前项目在Eureka状态页显示的信息
eureka.instance.instance-id=${spring.application.name}@${eureka.instance.ip-address}:${server.port}

启动项目后,在浏览器中,通过例如 http://localhost:8888/2020/09/1600672303652-886423607391600.png 这样的URL即可访问到此前成功上传的图片文件:

然后,在straw-gatewayapplication.properties中配置针对这个项目的转发规则:

然后,重启straw-gateway后,原本访问图片的URL改为 http://localhost/resource/2020/09/1600672303652-886423607391600.png 也可以访问成功:

5.服务器端向客户端响应上传的文件的URL

当客户端成功的上传了文件后,服务器端应该将上传的文件的URL响应到客户端,则客户端才可以正常的显示图片!

先在straw-api-questionapplication.properties中补充自定义配置:

并在QuestionController中读取该配置:

在上传成功之后,拼接出访问该图片的路径:

注意:不要忽略拼接时需要添加的/,否则会导致最终路径出错!

完成后,重启straw-api-question项目,再次上传图片,在控制台可以看到输出结果:

将以上路径复制,并补充 http://localhost 前缀,得到完整的例如 http://localhost/resource/2020/09/1600676569950-890689905787100.png 这样的URL,在浏览器中访问,即可看到刚才上传的图片!

当检查以上imageUrl的值无误后,在处理上传请求的方法的最后,返回该值到客户端:

则客户端上传图片后,当上传成功时,得到的响应结果大致是:

{
    "state": 2000,
    "data": "/resource/2020/09/1600676569950-890689905787100.png"
}

最后,在init_summernote.js中,可以测试输出结果:

完成后,分别重启straw-api-questionstraw-gateway项目,再次上传图片,在浏览器的控制台即可看到输出的图片路径:

6.在网页中显示新上传的文件

如果需要将上传的图片插入到summernote的编辑区,需要根据以上得到的路径值创建图片标签的对象,并调用summernote的API执行插入:

完成后,再次重启straw-gateway,发布一篇带图片的“问题”,在数据库中即可看到图片是通过路径来引用显示的,不再是此前的Base64编码!

7.添加测试数据

通过truncate命令清空questionquestion_taguser_question这3张表的数据,然后,通过页面登录,并发布不少于20篇问题,并且,至少有1个账号发布不少于15篇问题,以便于后续的功能开发及演示。

添加完成后,直接在question表中编辑数据,手动修改每个“问题”的hits值,尽量使得每个值都不同。

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值