使用DropZone+SpringBoot实现图片的上传和浏览

经常在项目中需要使用上传文件功能,找了不少前端上传组件,都不是很好用,今天尝试了一下DropZone,发现不错,顺便记录一下使用过程,方便后续查阅。在做开发的时候,经常需要调研一些技术,因此前后端都需要用到,为方便开发,这里采用传统的开发方式,没有做前后端分离,方便调试。前端采用HTML+Bootstrap+jQuery,后端采用SpringBoot2.6.3。

总体

新建一个SpringBoot程序,目录结构如下:
在这里插入图片描述
files:存放程序运行过程中的生成文件
logs:日志目录
src:源代码目录
uploads:上传文件目录
在这里插入图片描述
前端第三方文件放在/src/main/resources/static目录下,主要有bootstrap、dropzone和jquery,模版文件放在/src/main/resources/templates目录下,在这里注意调试时,经常需要修改HTML页面,需要快速将HTML页面编译到到运行目录/target/classes,这里使用快捷键CTRL+F9即可,前提是IDEA的自动构建项目要勾上
在这里插入图片描述

前端

首先从DropZone下载相关的js文件和css文件,或者直接让一些大模型给一个示例,我这里采用腾讯元宝,稍微修改一下,基本可以用,但是不少地方还是需要自己定制。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>File Upload</title>
    <link rel="stylesheet" href="./bootstrap/css/bootstrap.min.css">
    <script src="./bootstrap/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="./dropzone/dropzone.min.css">
    <script src="./dropzone/dropzone.min.js"></script>
    <script src="./jquery/jquery.min.js"></script>
    <style>
        .dropzone { padding: 3px 0 0 0; margin: 0 0 8px 0; text-align: center; overflow: hidden; width: 120px;
            min-height: 30px; height: 36px; border: #ccc solid 1px; }
        .dz-processing, .dz-button { display: none; }
        #fileList { display: flex; flex-wrap: wrap; gap: 10px; }
        .file-item { background-color: #f9f9f9; padding: 5px; border: 1px solid #ccc; border-radius: 0; cursor: pointer; }
    </style>
</head>
<body>
    <div class="container">
        <div style="padding:10px 0;">参看:https://www.dropzone.dev/</div>
        <div class="dropzone primary" id="my-dropzone">上传文件</div>
        <div id="message"></div>
        <div id="fileList"></div>
    </div>

<script>
    Dropzone.options.myDropzone = {
        url: '/upload',
        paramName: 'file',
        maxFiles:10,//一次性上传的文件数量上限
        maxFilesize: 20, //MB
        acceptedFiles: ".jpg,.gif,.png", //上传的类型
        parallelUploads: 3,
        dictMaxFilesExceeded: "您最多只能上传10个文件!",
        dictResponseError: '文件上传失败!',
        dictInvalidFileType: "你不能上传该类型文件,文件类型只能是*.jpg,*.gif,*.png。",
        dictFallbackMessage:"浏览器不受支持",
        dictFileTooBig:"文件过大上传文件最大支持.",
        previewTemplate: '<div></div>',
        showPreviewOnDrop: false,
        showPreviewOnUpload: false,
        init: function() {
            this.on('success', function(file, response) {
                $('#message').html('<p class="text-success">' + response.msg + '</p>');
                let json = JSON.parse(file.xhr.response);
                let fileName = '<div class="file-item" οnclick="showImage(\'' + json.data + '\')">' + json.data + '</div>';
                $('#fileList').prepend(fileName);
            });
            this.on('error', function(file, response) {
                $('#message').html('<p class="text-danger">Failed to upload file.</p>');
            });

            // 自定义文件显示方式
            this.on('addedfile', function(file) {
                console.log(file);
            });

            this.on('removedfile', function(file) {
                console.log(file);
                $('#fileList .file-item[data-dz-id="' + file.id + '"]').remove();
            });
        }
    };

    function showImage(url) {
        window.open('image/' + url, '_blank')
    }

    $(document).ready(function() {
        $.get('/images', function(res) {
            let array = res.data;
            let count = array.length;
            for (let i = 0; i < count; i ++) {
                let item = '<div class="file-item" οnclick="showImage(\'' + array[i] + '\')">' + array[i] + '</div>';
                $('#fileList').append(item);
            }
        })
    });
</script>
</body>
</html>

后端

后端主要实现三个接口,上传接口、获取图片列表接口、显示单个图片接口,分别对应/upload,/images,/image/{filename}

package org.example.imgtool.controller;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.example.imgtool.ImgtoolApplication;
import org.example.imgtool.utils.FileUtil;
import org.example.imgtool.utils.PathUtil;
import org.example.imgtool.utils.SnowflakeGenerator;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;

@Slf4j
@RestController
public class FileUploadController {

    private static final String UPLOAD_DIR = "uploads/";

    @GetMapping("/images")
    public ResponseEntity<JSONObject> getImages() {
        String imagesPath = PathUtil.getAppPath(ImgtoolApplication.class) + "files/images.txt";
        List<String> list = FileUtil.readFileToList(imagesPath);
        JSONObject json = new JSONObject(true);
        json.put("code", HttpStatus.OK.value());
        json.put("data", list);
        json.put("msg", "获取数据成功");
        return new ResponseEntity<>(json, HttpStatus.OK);
    }

    @GetMapping(value = "/image/{filename}", produces = MediaType.IMAGE_JPEG_VALUE)
    public ResponseEntity<byte[]> getRemoteImage(@PathVariable String filename) throws IOException {
        String imagePath = PathUtil.getAppPath(ImgtoolApplication.class) + "uploads/" + filename;
        byte[] bytes = FileUtil.getFileByteArray(imagePath);
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.IMAGE_JPEG));
        File file = new File(imagePath);
        if (file.exists()) {
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } else {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
    }

    @PostMapping("/upload")
    public ResponseEntity<JSONObject> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 检查上传目录是否存在,如果不存在则创建
            Path uploadPath = Paths.get(UPLOAD_DIR);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }

            // 保存文件到上传目录
            String fileName = file.getOriginalFilename();
            int index = fileName.lastIndexOf(".");
            String extension = fileName.substring(index);
            String newFileName = String.format("%d%s", SnowflakeGenerator.generatorId(), extension);
            log.debug(newFileName);
            Path filePath = uploadPath.resolve(newFileName);
            Files.copy(file.getInputStream(), filePath);
            // 文件名写入文件
            String imagesPath = PathUtil.getAppPath(ImgtoolApplication.class) + "files/images.txt";
            File f = new File(imagesPath);
            List<String> list = null;
            if (f.exists()) {
                list = FileUtil.readFileToList(imagesPath);
            } else {
                list = Collections.emptyList();
            }
            list.add(0, newFileName);
            FileUtil.writeFile(imagesPath, list);

            JSONObject json = new JSONObject(true);
            json.put("code", HttpStatus.OK.value());
            json.put("data", newFileName);
            json.put("msg", "上传成功" + newFileName);
            return new ResponseEntity<>(json, HttpStatus.OK);
        } catch (IOException e) {
            log.error("Upload image fail : {}", e.getMessage());
            JSONObject json = new JSONObject(true);
            json.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
            json.put("msg", "上传失败");
            return new ResponseEntity<>(json, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

配置文件如下,注意这里spring.thymeleaf.cache一定要配置为false,这样前面提到的CTRL+F9就可以实时调试,方便前端调试代码。

spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.excluded-view-names=
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
server.port=8080
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB

效果

最后实现效果如下
在这里插入图片描述

源代码

转到https://download.csdn.net/download/Angushine/89672489下载即可。

  • 25
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

angushine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值