使用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, //MBacceptedFiles: ".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下载即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/408644.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 运算符

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C# 有丰富的内置运算符&#xff0c;分为一下六类&#xff1a; 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 算术运算符 C# 支持的所有算术运算符。假设变量 A 的值为 10&#xff0c;变量 B 的值…

安全面试常见问题任意文件下载

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 1.1 任意文件下…

旅游社交小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;每日签到管理&#xff0c;景点推荐管理&#xff0c;景点分类管理&#xff0c;防疫查询管理&#xff0c;美食推荐管理&#xff0c;酒店推荐管理&#xff0c;周边推荐管理 微信端账…

《数据结构》顺序表+算法代码+动画演示-C语言版

目录 顺序表概念 顺序表初始化 顺序表销毁 顺序表尾插 顺序表尾删 顺序表头删 顺序表头插 顺序表pos位置插入 顺序表pos位置删除 顺序表全部代码如下&#xff1a; 顺序表概念 顺序表是用一段 物理地址连续 的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下…

多个程序监听不同网卡的相同端口、相同网卡不同IP的相同端口

1 概述 一个主机上的多个程序监听同一个端口&#xff0c;是否一定存在冲突&#xff1f;如果是多网卡、单网卡多IP的情景下&#xff0c;多个程序是可以独立监听的。 2 多个程序监听不同网卡的相同端口 3 多个程序监听同一个网卡不同IP的相同端口 4 小结 多个程序监听同一个网…

【C语言】常见文件操作

文件的常见操作 #include<stdio.h>// 由于devc代码编码为ANCI&#xff0c;故读取的文件中若有中文&#xff0c;请设置文件编码为ANCI&#xff0c;否则会乱码 // 读文件 void test1() {char ch;FILE *fp; // 创建文件指针fp fopen("./file.txt", "r"…

pycharm修改文件大小限制

场景&#xff1a; 方法&#xff1a; 打开pycharm 安装目录下的idea.properties 增加配置项&#xff1a;idea.max.intellisense.filesize99999

java后端请求与响应总结

get 请求&#xff1a;将参数写在请求路径中&#xff08;请求路径跟一个&#xff1f;后面跟参数多个参数之间用&连接&#xff09; post 请求&#xff1a;将参数写在请求体中中 一、请求 1.简单参数 如 传一个或两个字符串、整数等 例如串一个用户名和密码 如果传入的数…

【自动驾驶】控制算法(四)坐标变换与横向误差微分方程

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

从法律风险的角度来看,项目经理遇到不清楚或不明确问题时的处理

大家好&#xff0c;我是不会魔法的兔子&#xff0c;在北京从事律师工作&#xff0c;日常分享项目管理风险预防方面的内容。 序言 在项目开展过程中&#xff0c;有时候会遇到一些不清楚或不明确的状况&#xff0c;但碍于项目进度的紧迫性&#xff0c;不得不硬着头皮做决策&…

Golang | Leetcode Golang题解之第368题最大整除子集

题目&#xff1a; 题解&#xff1a; func largestDivisibleSubset(nums []int) (res []int) {sort.Ints(nums)// 第 1 步&#xff1a;动态规划找出最大子集的个数、最大子集中的最大整数n : len(nums)dp : make([]int, n)for i : range dp {dp[i] 1}maxSize, maxVal : 1, 1fo…

CMake构建学习笔记4-libjpeg库的构建

libjpeg是一个广泛使用的开源库&#xff0c;用于处理JPEG&#xff08;Joint Photographic Experts Group&#xff09;图像格式的编码、解码、压缩和解压缩功能&#xff0c;是许多图像处理软件和库的基础。 libjpeg本身的构建没什么特别的&#xff0c;不过值得说道的是libjpeg存…

[Other]-安装ruby、ascli、ascp

最近新接到这样一个需求&#xff0c;将生物原始数据上传到某中心&#xff0c;其中用到ascp命令&#xff0c;阴差阳错的装了ruby、ascli&#xff0c;这里就都一并介绍下安装方式&#xff0c;由于服务器老旧默认安装时ruby2.0&#xff0c;又 升级到2.7等引发的一系列问题&#xf…

力扣(动态规划)-343整数拆分;96不同的二叉搜索树

整数拆分 题目&#xff1a; 给定⼀个正整数 n&#xff0c;将其拆分为⾄少两个正整数的和&#xff0c;并使这些整数的乘积最⼤化。 返回你可以获得的最⼤乘积。 示例 1: 输⼊: 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输⼊: 10 输出: 36 解释: 10 3 3 4, 3 3…

Python 递归(recursion) 和 迭代(iteration)

递归 (recursion) 是指在函数的定义中使用函数自身的方法&#xff0c;直观上来看&#xff0c;就是某个函数自己调用自己。递归的基本思想就是把规模大的问题转化为规模小的相同的子问题来解决。 在函数实现时&#xff0c;因为大问题和小问题是一样的问题&#xff0c;因此大问题…

一款人性化的终端用户界面工具

A collection of human friendly terminal user interface. Screenshot • Installation • Usage • Configuration • Thanks 截图 历史文件预览 注意: find file 依赖 fzf. file browser依赖 ranger / lf / … 安装 git clone https://github.com/StubbornVegeta/Sta…

3秒内搞定服务器端口扫描!用RustScan快速查看开放端口

文章目录 3秒内搞定服务器端口扫描&#xff01;用RustScan快速查看开放端口1. RustScan简介2. RustScan特点3. RustScan的基本使用3.1 创建alias别名3.2 基本用法3.3 常用参数说明3.4 示例4. 注意事项 最近开始公众号文章也开始同步更新了&#xff0c;对Java、大数据、人工智能…

字节微前端框架Garfish

Garfish 是字节跳动开源的微前端框架&#xff0c;旨在应对现代 Web 应用在前端生态繁荣与应用日益复杂化背景下的挑战。本文将介绍如何使用 Garfish&#xff0c;提供代码示例&#xff0c;并与另一流行的微前端框架 Qiankun 进行对比分析。 安装 Garfish 首先&#xff0c;安装…

大模型对齐:DPO vs PPO

现在这些大型语言模型&#xff08;LLMs&#xff09;&#xff0c;可真是火得不行&#xff0c;各行各业都离不开它们了。它们能处理和写出跟我们差不多的文本&#xff0c;这让自然语言处理、写东西、还有客服这些领域都焕然一新。不过呢&#xff0c;这技术进步的同时也带来了一个…

Unity+Addressable

前期准备 下载一个hfs本地服务器&#xff0c;打开即可 HFS ~ HTTP 文件服务器 (rejetto.com) 1.安装Addressable插件 创建组 2.使用图片创建预制体 放入Addressable Groups内 3.右键 新建组 创建预制体t拖拽放入新建组里 新组命名为Gameobject 简化名称 4.创建一个测试脚本 …