文章目录

  • SpringBoot 简易文件服务器
    • 确定需求
    • 核心技术
    • html 上传:文件上传
    • html 上传:多文件上传
    • html 上传:图片上传 - 压缩
    • ajax 上传:FormData 上传
    • 其他:将指定文件夹打包为 zip
    • 其他:生成二维码
    • 常用:下载文件
    • 工具类:FileUtils
    • 工具类:QrCodeUtils
  • 微信公众号

SpringBoot 简易文件服务器

确定需求

首先,需求分析是核心一点,明白我们需要做成什么效果,再去想使用什么手段实现,当然,这里实现的核心工鞥就是对文件的操作:上传、下载、压缩、打包等

项目源码

核心技术

  • 框架基础:采用 spring boot 2.2.6.RELEASE,因为几乎零配置文件,使用起来更加方便
  • 项目基础:采用 apache-maven-3.6.0,使用坐标极大的简化了 jar 的配置与安装
  • 实体插件:采用 lombok,这个插件能使用注解的形式构建实体类相关的各种操作
  • 图像压缩:采用 Thumbnailator,目前网上算是比较用的普遍的图片压缩工具
  • json 序列化:采用 fastjson,序列化工具使用 alibaba 提供的工具类
  • 二维码工具:采用 zxing,谷歌提供的生成二维码的工具类
  • 文件操作必备的依赖:commons-io、commons-codec

html 上传:文件上传

html 的表单上传文件,应该是开发中所有人接触的第一种上传方法,首先我们准备一个 form 表单,并设置对应的上传路径,表单中有一个 input 属性,type 类型为 file 的输入框,然后设置一个提交按钮即可

<form method="POST" action="/upload" enctype="multipart/form-data"><input type="file" name="file"/><br/><br/>文件夹名称:<input type="text" name="folder" value="upload"/><br/><br/><input type="submit" value="Submit"/>
</form>

相对应的,后台接收这个请求中的 file 对象,和 folder 参数,参数说明:

参数 类型 默认值 说明
file file input 输入框所选取的文件对象
folder text upload 存放服务器的文件夹

接收并处理请求,我这里将所有的代码放在 serviceImpl 中编写,因为 controller 控制层理论上只做控制处理,没有逻辑业务

    @ApiOperation("文件上传")@PostMapping("/upload")public ResponseResult upload(@RequestParam("file") MultipartFile file,@RequestParam(value = "folder", defaultValue = "default") String folder) {return ResponseResult.success(fileService.upload(file, folder));}

service 接口层返回了一个 string 字符串类型,实际上就是上传成功后返回的访问路径

String upload(MultipartFile file, String folder);

serviceImpl 实现类中处理请求分为三个阶段:验证参数、处理文件、返回访问路径

    @Overridepublic String upload(MultipartFile file, String folder) {if (file.isEmpty()) {throw new FileVerifyException(FileEnums.NOT_FOUND.getInfo());}return FileUtils.upload(file, folder).getRelativePath();}

这里我们看到最后使用工具类 FileUtils 处理文件,工具类的内容我就不一步步分析了,其中涉及了异常处理、常量配置等,不做过多的介绍

html 上传:多文件上传

对于前端来书,多文件上传与单文件上传的区别在于,数量上的不同,选取多个文件,我们可以通过 input 的 multiple="multiple"属性控制是否可以多选

<form method="POST" action="/uploadMore" enctype="multipart/form-data"><input type="file" name="file" multiple="multiple"/><br/><br/>文件夹名称:<input type="text" name="folder" value="uploadFile"/><br/><br/><input type="submit" value="Submit"/>
</form>

参数说明:

参数 类型 默认值 说明
file file 增加了 multiple="multiple"之后,选择多个文件实际上为一个数组对象
folder text upload 存放服务器的文件夹

接收并处理请求,我这里将所有的代码放在 serviceImpl 中编写,因为 controller 控制层理论上只做控制处理,没有逻辑业务

    @ApiOperation("多文件上传")@PostMapping("/uploadMore")public ResponseResult uploadMore(@RequestParam("file") MultipartFile[] files,@RequestParam(value = "folder", defaultValue = "default") String folder) {return ResponseResult.success(fileService.uploadMore(files, folder));}

service 接口层返回了一个 string 字符串类型,实际上就是上传成功后返回的访问路径,多个路径之间使用“,”分隔开

String uploadMore(MultipartFile[] files, String folder);

serviceImpl 实现类中处理请求分为三个阶段:验证参数、处理文件、返回访问路径

    @Overridepublic String uploadMore(MultipartFile[] files, String folder) {if (files.length == 0) {throw new FileVerifyException(FileEnums.NOT_FOUND.getInfo());}List<String> list = new ArrayList<>();for (MultipartFile file : files) {list.add(FileUtils.upload(file, folder).getRelativePath());}if (CollectionUtils.isEmpty(list)) {throw new FileSaveException(FileEnums.SAVE_ERROR.getInfo());}return StringUtils.join(list, FilePathConst.SEPARATOR);}

我这里多文件上传实际上是遍历文件单个上传,然后拼接返回值,如果有更多的办法,还请连一下作者哟

html 上传:图片上传 - 压缩

图片上传并且压缩,这个是非常常见的功能需求,如果说客户上传一 10 张普通的图片大小为 10M,1000 个客户上传的总大小为 10G 不到一点,对于服务器而言,存在一个的空间限制,而且访问图片的同时,由于图片过大,导致加载速度过慢,这些问题都会导致客户的体验度下降,这里我们就可以考虑在不影响图片清晰度的情况下,降低图片占用的空间

<form method="POST" action="/uploadByThumbnail" enctype="multipart/form-data"><input type="file" name="file" multiple="multiple"/><br/><br/>文件夹名称:<input type="text" name="folder" value="uploadByThumbnail"/><br/><br/>是否保存原图:<input type="radio" name="saveOld" value="true" checked>是</label><input type="radio" name="saveOld" value="false">否</label><br/><br/><input type="submit" value="Submit"/>
</form>

参数说明:

参数 类型 默认值 说明
file file input 输入框所选取的文件对象
folder text uploadByThumbnail 存放服务器的文件夹
saveOld boolean true 是否保存原图

相对应的控制层处理和普通文件上传是类似的,只是多了一个参数

    @ApiOperation("图片上传并压缩")@PostMapping("/uploadByThumbnail")public ResponseResult uploadByThumbnail(@RequestParam("file") MultipartFile[] files,@RequestParam(value = "folder", defaultValue = "default") String folder,@RequestParam(value = "saveOld", defaultValue = "true") boolean saveOld) {return ResponseResult.success(fileService.uploadByThumbnail(files, folder, saveOld));}

service 接口同样返回访问路径,多个路径使用“,”隔开

String uploadByThumbnail(MultipartFile[] files, String folder, boolean saveOld);

serviceImpl 接口实现类处理请求

    @Overridepublic String uploadByThumbnail(MultipartFile[] files, String folder, boolean saveOld) {if (files.length == 0) {throw new FileVerifyException(FileEnums.NOT_FOUND.getInfo());}List<String> list = new ArrayList<>();for (MultipartFile file : files) {list.add(FileUtils.uploadByThumbnail(file, folder, saveOld).getRelativePath());}if (CollectionUtils.isEmpty(list)) {throw new FileSaveException(FileEnums.SAVE_ERROR.getInfo());}return StringUtils.join(list, FilePathConst.SEPARATOR);}

多图片上传并压缩也是同样的道理,这里需要注意一点,比如我上传两个图片,并设置为保存原图,实际上服务器保存了 4 张图片,服务器的返回值的格式为:

{"code": 200,"message": "OK","count": 0,"data": "/uploadByThumbnail/2020/04/19/95f3da7a-f41b-4767-a229-a64108898f9a.png|/uploadByThumbnail/2020/04/19/95f3da7a-f41b-4767-a229-a64108898f9a_thumbnail.png,/uploadByThumbnail/2020/04/19/4be30b69-3205-462d-9f10-c7e5e851f3bc.png|/uploadByThumbnail/2020/04/19/4be30b69-3205-462d-9f10-c7e5e851f3bc_thumbnail.png"
}

解析返回值:首先我们通过“,”分隔开,得到每张图片的处理结果,然后在使用“|”将返回结果分隔开,得到原图的访问路径和压缩图片的访问路径

ajax 上传:FormData 上传

工作中实际上直接使用 html 上传几乎是没有的,因为通常我们要将上传成功返回的访问路径存入数据库中,接下来就是怎么样拿到上传返回的信息,这里我们举例通过 ajax 的 FormData 的方式上传

FormData 的 API 文档参考

先引入 jquery,最好还是自己下载到本地放入项目当中

<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

这里我们不需要表单了,直接给 input 绑定点击事件即可

<input type="file" name="file" id="formDataUpload"/><br/><br/>
文件夹名称:<input type="text" name="folder" value="ajax" id="formDataFolder"/><br/><br/>
<input type="button" value="Submit" id="formDataButton"/>

然后监听点击事件,我们的上传路径直接使用“html 上传:文件上传”模块时使用的即可,主要是拿到返回值

    $('#formDataButton').on('click', function () {var formData = new FormData();formData.append("file", $('#formDataUpload')[0].files[0]);formData.append("folder", $.trim($('#formDataFolder').val()));$.ajax({url: '/upload',dataType: 'json',type: 'POST',data: formData,processData: false, // 使数据不做处理contentType: false, // 不要设置Content-Type请求头success: function (res) {console.log(res);if (res.code == 200) {alert("上传成功");} else {alert("上传失败");}},error: function (res) {console.log(res);}});});

请求完成之后,在控制台即可知道返回的结果,使用访问前缀拼接上访问路径即可实现访问图片

其他:将指定文件夹打包为 zip

这个案例是实际开发中作者遇到的问题,完整需求是:通过请求参数,生成 1 万张二维码,并下载二维码,这里我们就需要将这 1 万张二维码压缩,并生成 zip,然后返回一个下载路径即可

首先准备一个表单

<form method="POST" action="/folderToZip">待压缩的文件路径:<input type="text" name="folderPath" /><br/><br/>压缩后存放路径:<input type="text" name="zipPath"/><br/><br/>压缩后文件的名称:<input type="text" name="fileName"/><br/><br/><input type="submit" value="Submit"/>
</form>

参数说明:路径都是相对于文件服务器的根目录书写的,例如:/uploadByThumbnail/2020/04/19,不用写绝对路径

参数 类型 默认值 说明
folderPath string 待压缩的文件路径
zipPath string 压缩后存放路径
fileName string 压缩后文件的名称

控制层直接将请求交个 service 处理

    @ApiOperation("将指定文件夹打包为zip")@PostMapping("/folderToZip")public ResponseResult folderToZip(String folderPath, String zipPath, String fileName) {return ResponseResult.success(fileService.folderToZip(folderPath, zipPath, fileName));}

service 接口,同样的返回打包完成之后的访问路径,使用访问前缀拼接上访问路径可即可下载 zip 包

String folderToZip(String folderPath, String zipPath, String fileName);

serviceImpl 实现类直接调用工具类处理

    @Overridepublic String folderToZip(String folderPath, String zipPath, String fileName) {boolean flag = FileUtils.folderToZip(folderPath, zipPath, fileName);if (flag) {return zipPath + File.separator + fileName + ".zip";}throw new FileSaveException(FileEnums.FILE_ZIP_ERROR.getInfo());}

其他:生成二维码

生成二维码也是很常见的需要,通常情况下,我们将一些信息存放于二维码中,用户直接扫码即可读取相关信息,一般情况下,信息都是经过加密处理的,比如说考试的试卷信息,每张试卷都是独一无二的编号,存放于这个二维码中,考虑安全性,是不能直接被扫码查看的

<form method="POST" action="/createQrCode">文件夹名称:<input type="text" name="folder" value="createQrCode"/><br/><br/>二维码内容:<input type="text" name="content" value="测试二维码内容"/><br/><br/><input type="submit" value="Submit"/>
</form>

参数说明:

参数 类型 默认值 说明
folder text createQrCode 存放服务器的文件夹
content string 测试二维码内容 二维码内容

控制层

    @ApiOperation("生成二维码")@PostMapping("/createQrCode")public ResponseResult createQrCode(@RequestParam(value = "content", defaultValue = "默认二维码内容")String content,@RequestParam(value = "folder", defaultValue = "default") String folder) {return ResponseResult.success(fileService.createQrCode(content, folder));}

service 接口同样是返回二维码的访问地址

String createQrCode(String content, String folder);

serviceImpl 通过谷歌提供的二维码生成工具类生成二维码,这里的工具类被再次封装过

    @Overridepublic String createQrCode(String content, String folder) {try {String datePath = FileUtils.getDatePath(folder, FileUtils.DATE_TYPE_SLASH);String fileName = String.valueOf(UUID.randomUUID()).concat(".jpg");// 详情查看工具类,功能非常强大QrCodeUtils.encode(content, null, FilePathConst.SAVE_POSITION + datePath, fileName, false);return datePath + fileName;} catch (Exception e) {e.printStackTrace();throw new FileSaveException(FileEnums.QRCODE_CREATE_ERROR.getInfo());}}

常用:下载文件

下载文件时一个核心功能,有上传一定有下载,毋庸置疑。当我们上传文件的同时,为了避免文件名重复等为题,我们会给文件名重命名为随机名字,比如我这里使用的 Java UUID 工具:

// 文件名
String fileName = String.valueOf(UUID.randomUUID());

那么问题来了,我下载的时候要指定文件名字,下面就是实现方式

<form method="POST" action="/downloadFile">文件路径:<input type="text" name="filePath" value="/default/2020/04/12/1fee42a4-f902-488b-87c6-ca82ea6bc958.jpg"/><br/><br/>下载名称:<input type="text" name="fileName" value="我是下载文件的名字.jpg"/><br/><br/><input type="submit" value="Submit"/>
</form>

参数说明:

参数 类型 默认值 说明
filePath string /default/2020/04/12/1fee42a4-f902-488b-87c6-ca82ea6bc958.jpg 访问的相对路径,我这里的默认值是方便测试,之前上传的图片的访问地址
fileName string 我是下载文件的名字.jpg 下载保存的文件名

这里有个细节,很多网上的教程下载中文名字时会发生乱码,我这一步故意设置为中文名字,实际上后台已经解决了这个问题
controller 控制层接收请求

    @ApiOperation("下载文件")@PostMapping("downloadFile")public void downloadFile(String filePath, String fileName, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {fileService.downloadFile(filePath, fileName, request, response);}

service 接口没有返回值,因为我这里是下载文件

void downloadFile(String filePath, String fileName, HttpServletRequest request, HttpServletResponse response);

serviceImpl 实现调用工具类实现下载

    @Overridepublic void downloadFile(String filePath, String fileName, HttpServletRequest request, HttpServletResponse response) {FileUtils.download(filePath, fileName, request, response);}

工具类:FileUtils

文件操作工具类

/*** 文件上传工具类** @author Tellsea* @date 2019/8/20*/
@Slf4j
public class FileUtils {public static final String DATE_TYPE_SLASH = "yyyy" + File.separator + "MM" + File.separator + "dd";public static FileInfo upload(MultipartFile file, String folder) {// 文件名String fileName = String.valueOf(UUID.randomUUID());// 文件类型String fileType = file.getOriginalFilename().substring(file.getOriginalFilename().indexOf("."));// 时间文件夹String datePath = getDatePath(folder, DATE_TYPE_SLASH);// 相对路径String relativePath = datePath.concat(fileName).concat(fileType);// 绝对路径String destFile = FilePathConst.SAVE_POSITION.concat(datePath);File f = new File(destFile);if (!f.exists() && !f.isDirectory()) {f.mkdirs();}// 文件全路径destFile = destFile.concat(fileName).concat(fileType);try {byte[] bytes = file.getBytes();Path path = Paths.get(destFile);Files.write(path, bytes);log.info("上传成功...");log.info("相对路径:{}", relativePath);log.info("绝对路径:{}", destFile);return new FileInfo().setFileName(fileName).setFileType(fileType).setDatePath(datePath).setDestFile(destFile).setRelativePath(relativePath);} catch (Exception e) {throw new FileSaveException(FileEnums.SAVE_ERROR.getInfo());}}/*** 压缩上传** @param file* @param folder* @param saveOld 是否保存原图* @return*/public static FileInfo uploadByThumbnail(MultipartFile file, String folder, boolean saveOld) {FileInfo fileInfo = upload(file, folder);try {File toFile;if (saveOld) {String path = FilePathConst.SAVE_POSITION.concat(fileInfo.getDatePath()).concat(fileInfo.getFileName()).concat(FilePathConst.THUMBNAIL_SUFFIX).concat(fileInfo.getFileType());toFile = new File(path);String newFilePath = fileInfo.getDatePath().concat(fileInfo.getFileName()).concat(FilePathConst.THUMBNAIL_SUFFIX).concat(fileInfo.getFileType());fileInfo.setRelativePath(fileInfo.getRelativePath().concat(FilePathConst.THUMBNAIL_SEPARATOR) + newFilePath);} else {toFile = new File(fileInfo.getDestFile());}Thumbnails.of(fileInfo.getDestFile()).imageType(BufferedImage.TYPE_INT_ARGB)// 指定图片大小    0-1f  1f是原图.scale(0.5f)// 图片质量  0-1f  1f是原图.outputQuality(0.8f).toFile(toFile);} catch (Exception e) {e.printStackTrace();throw new FileThumbnailsException(FileEnums.THUMBNAILS_ERROR.getInfo());}return fileInfo;}/*** 获得保存路径** @param folder* @param dateFormat* @return 例如:/aaa/2020/04/11/*/public static String getDatePath(String folder, String dateFormat) {String date = new SimpleDateFormat(dateFormat).format(new Date());StringBuffer path = new StringBuffer();path.append(File.separator).append(folder).append(File.separator).append(date).append(File.separator);return path.toString();}/*** 将指定文件夹打包** @param sourceFilePath :待压缩的文件路径* @param zipFilePath    :压缩后存放路径* @param fileName       :压缩后文件的名称* @return*/public static boolean folderToZip(String sourceFilePath, String zipFilePath, String fileName) {boolean flag = false;File sourceFile = new File(sourceFilePath);FileInputStream fis = null;BufferedInputStream bis = null;FileOutputStream fos = null;ZipOutputStream zos = null;if (!sourceFile.exists()) {throw new FileVerifyException("待压缩的文件目录:" + sourceFilePath + "不存在.");} else {try {File zipFile = new File(zipFilePath + File.separator + fileName + ".zip");if (zipFile.exists()) {zipFile.delete();}File[] sourceFiles = sourceFile.listFiles();if (null == sourceFiles || sourceFiles.length < 1) {throw new FileVerifyException("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩.");} else {fos = new FileOutputStream(zipFile);zos = new ZipOutputStream(new BufferedOutputStream(fos));byte[] bufs = new byte[1024 * 10];for (int i = 0; i < sourceFiles.length; i++) {//创建ZIP实体,并添加进压缩包ZipEntry zipEntry = new ZipEntry(sourceFiles[i].getName());zos.putNextEntry(zipEntry);//读取待压缩的文件并写进压缩包里fis = new FileInputStream(sourceFiles[i]);bis = new BufferedInputStream(fis, 1024 * 10);int read = 0;while ((read = bis.read(bufs, 0, 1024 * 10)) != -1) {zos.write(bufs, 0, read);}}flag = true;}} catch (FileNotFoundException e) {e.printStackTrace();throw new RuntimeException(e);} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);} finally {//关闭流try {if (null != bis) {bis.close();}if (null != zos) {zos.close();}} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);}}}return flag;}/*** 得到图片字节流 数组大小*/public static byte[] readStream(InputStream inStream) throws Exception {ByteArrayOutputStream outStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len = -1;while ((len = inStream.read(buffer)) != -1) {outStream.write(buffer, 0, len);}outStream.close();inStream.close();return outStream.toByteArray();}/*** 将文件转换成Byte数组** @param file* @return*/public static byte[] getBytesByFile(File file) {try {FileInputStream fis = new FileInputStream(file);ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);byte[] b = new byte[1000];int n;while ((n = fis.read(b)) != -1) {bos.write(b, 0, n);}fis.close();byte[] data = bos.toByteArray();bos.close();return data;} catch (Exception e) {e.printStackTrace();}return null;}/*** MultipartFile转File** @param param* @return*/public static File transfer(MultipartFile param) {if (!param.isEmpty()) {File file = null;try {InputStream in = param.getInputStream();file = new File(param.getOriginalFilename());OutputStream out = new FileOutputStream(file);int bytesRead = 0;byte[] buffer = new byte[8192];while ((bytesRead = in.read(buffer, 0, 8192)) != -1) {out.write(buffer, 0, bytesRead);}in.close();out.close();return file;} catch (Exception e) {e.printStackTrace();return file;}}return null;}/*** 获取指定文件的输入流** @param logoPath 文件的路径* @return*/public static InputStream getResourceAsStream(String logoPath) {return FileUtils.class.getResourceAsStream(logoPath);}/*** 下载文件** @param filePath 访问路径* @param fileName 下载文件名* @param request* @param response*/public static void download(String filePath, String fileName, HttpServletRequest request, HttpServletResponse response) {File file = new File(FilePathConst.SAVE_POSITION + filePath);if (file.exists()) {response.setHeader("content-type", "application/octet-stream");response.setContentType("application/octet-stream");// 下载文件能正常显示中文try {response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}// 实现文件下载byte[] buffer = new byte[1024];FileInputStream fis = null;BufferedInputStream bis = null;try {fis = new FileInputStream(file);bis = new BufferedInputStream(fis);OutputStream os = response.getOutputStream();int i = bis.read(buffer);while (i != -1) {os.write(buffer, 0, i);i = bis.read(buffer);}} catch (Exception e) {throw new FileSaveException(FileEnums.DOWNLOAD_ERROR.getInfo());} finally {if (bis != null) {try {bis.close();} catch (IOException e) {e.printStackTrace();}}if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}} else {throw new FileSaveException(FileEnums.DOWNLOAD_NOT_FOUND_ERROR.getInfo());}}public static void main(String[] args) {String path = "D:\\Workspace\\IDEAWorkspace\\file-server\\file-static\\uploadByThumbnail\\2020\\04\\12";folderToZip(path, path, "test");}
}

工具类:QrCodeUtils

生成二维码工具类

/*** 二维码生成工具类** @author user* @date 2018/12/5*/
public class QrCodeUtils {private static final String CHARSET = "utf-8";public static final String FORMAT = "JPG";/*** 二维码尺寸*/private static final int QRCODE_SIZE = 300;/*** LOGO宽度*/private static final int LOGO_WIDTH = 60;/*** LOGO高度*/private static final int LOGO_HEIGHT = 60;/*** 生成二维码** @param content      二维码内容* @param logoPath     logo地址* @param needCompress 是否压缩logo* @return 图片* @throws Exception*/public static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, CHARSET);hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,hints);int width = bitMatrix.getWidth();int height = bitMatrix.getHeight();BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x = 0; x < width; x++) {for (int y = 0; y < height; y++) {image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);}}if (logoPath == null || "".equals(logoPath)) {return image;}// 插入图片QrCodeUtils.insertImage(image, logoPath, needCompress);return image;}/*** 插入LOGO** @param source       二维码图片* @param logoPath     LOGO图片地址* @param needCompress 是否压缩* @throws IOException*/private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws IOException {InputStream inputStream = null;try {inputStream = FileUtils.getResourceAsStream(logoPath);Image src = ImageIO.read(inputStream);int width = src.getWidth(null);int height = src.getHeight(null);if (needCompress) {// 压缩LOGOif (width > LOGO_WIDTH) {width = LOGO_WIDTH;}if (height > LOGO_HEIGHT) {height = LOGO_HEIGHT;}Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();// 绘制缩小后的图g.drawImage(image, 0, 0, null);g.dispose();src = image;}// 插入LOGOGraphics2D graph = source.createGraphics();int x = (QRCODE_SIZE - width) / 2;int y = (QRCODE_SIZE - height) / 2;graph.drawImage(src, x, y, width, height, null);Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);graph.setStroke(new BasicStroke(3f));graph.draw(shape);graph.dispose();} catch (IOException e) {e.printStackTrace();throw new RuntimeException(e);} finally {if (inputStream != null) {inputStream.close();}}}/*** 生成二维码(内嵌LOGO)* 二维码文件名随机,文件名可能会有重复** @param content      内容* @param logoPath     LOGO地址* @param destPath     存放目录* @param needCompress 是否压缩LOGO* @throws Exception*/public static String encode(String content, String logoPath, String destPath, boolean needCompress) throws Exception {BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);mkdirs(destPath);String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase();ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));return fileName;}/*** 生成二维码(内嵌LOGO)* 调用者指定二维码文件名** @param content      内容* @param logoPath     LOGO地址* @param destPath     存放目录* @param fileName     二维码文件名* @param needCompress 是否压缩LOGO* @throws Exception*/public static String encode(String content, String logoPath, String destPath, String fileName, boolean needCompress) throws Exception {BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);mkdirs(destPath);fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length())+ "." + FORMAT.toLowerCase();ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));return fileName;}/*** 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.* (mkdir如果父目录不存在则会抛出异常)** @param destPath 存放目录*/public static void mkdirs(String destPath) {File file = new File(destPath);if (!file.exists() && !file.isDirectory()) {file.mkdirs();}}/*** 生成二维码(内嵌LOGO)** @param content  内容* @param logoPath LOGO地址* @param destPath 存储地址* @throws Exception*/public static String encode(String content, String logoPath, String destPath) throws Exception {return QrCodeUtils.encode(content, logoPath, destPath, false);}/*** 生成二维码** @param content      内容* @param destPath     存储地址* @param needCompress 是否压缩LOGO* @throws Exception*/public static String encode(String content, String destPath, boolean needCompress) throws Exception {return QrCodeUtils.encode(content, null, destPath, needCompress);}/*** 生成二维码** @param content  内容* @param destPath 存储地址* @throws Exception*/public static String encode(String content, String destPath) throws Exception {return QrCodeUtils.encode(content, null, destPath, false);}/*** 生成二维码(内嵌LOGO)** @param content      内容* @param logoPath     LOGO地址* @param output       输出流* @param needCompress 是否压缩LOGO* @throws Exception*/public static void encode(String content, String logoPath, OutputStream output, boolean needCompress)throws Exception {BufferedImage image = QrCodeUtils.createImage(content, logoPath, needCompress);ImageIO.write(image, FORMAT, output);}/*** 生成二维码** @param content 内容* @param output  输出流* @throws Exception*/public static void encode(String content, OutputStream output) throws Exception {QrCodeUtils.encode(content, null, output, false);}
}

微信公众号

【SpringBoot学习】35、SpringBoot 简易文件服务器相关推荐

  1. 超详细的springBoot学习教程,springboot学习看这篇就够了

    springBoot学习 https://docs.spring.io/spring-boot/docs/2.2.6.RELEASE/reference/html/index.html (官方文档) ...

  2. 【Springboot学习】SpringBoot集成Shiro前后端分离使用redis做缓存【个人博客搭建】

    shiro-redis 目录 shiro-redis 下载 shiro-core/jedis 版本对比图 使用前 如何配置? 设置文件 Redis 独立 Redis哨兵 Redis 集群 Spring ...

  3. SpringBoot学习-千里之行始于足下

    Springboot SpringBoot 学习 1.SpringBoot概述 2.SpringBoot入门 3.Java配置 SpringBoot 学习 1.SpringBoot概述 关于Sprin ...

  4. 狂神说——SpringBoot学习

    spring官网 SpringBoot官网 spring-security版本下载 狂神官网学习 也可以搜索B站 (狂神说) 学习网站:https://www.bilibili.com/video/B ...

  5. springboot学习笔记:12.解决springboot打成可执行jar在linux上启动慢的问题

    springboot学习笔记:12.解决springboot打成可执行jar在linux上启动慢的问题 参考文章: (1)springboot学习笔记:12.解决springboot打成可执行jar在 ...

  6. SpringBoot学习笔记(3):静态资源处理

    SpringBoot学习笔记(3):静态资源处理 在web开发中,静态资源的访问是必不可少的,如:Html.图片.js.css 等资源的访问. Spring Boot 对静态资源访问提供了很好的支持, ...

  7. 目录:SpringBoot学习目录

    SpringBoot配套源码地址:gitee.com/hengboy/spr- SpringCloud配套源码地址:gitee.com/hengboy/spr- SpringBoot相关系列文章请访问 ...

  8. springboot学习笔记(五)

    一丶注值方式 1.在application.properties文件中注值 首先我们将application.yml中的学生名字和年龄给注释掉,来验证在applic.properties的注值方式. ...

  9. SpringBoot学习笔记(4)----SpringBoot中freemarker、thymeleaf的使用

    1. freemarker引擎的使用 如果你使用的是idea或者eclipse中安装了sts插件,那么在新建项目时就可以直接指定试图模板 如图: 勾选freeMarker,此时springboot项目 ...

  10. Github 上 10 个值得学习的 Springboot 开源项目

    Spring Boot 几乎继承了所有 Spring 框架的优点,同时还可以让项目的配置更简化.编码更简化.部署更方便.近两年受到很多开发者的追捧,也是火热的不行! 下面给大家整理了 10 个 Git ...

最新文章

  1. 28岁硕士女程序员想分手!对象专科学历,北京土著,失业3个月找不到工作!遭网友群嘲!...
  2. ESLint里的规则教会我,无规矩 不编程
  3. ant-design-pro Login 组件 实现 rules 验证
  4. 数字证书及CA的扫盲介绍
  5. OpenCV android sdk配置OpenCV android NDK开发实例
  6. Redis的安装与简单部署
  7. linux中的变量文件路径,Linux库文件和Shell可执行程序命令文件搜索路径变量的设置...
  8. 关于 安利商品“欺诈”案
  9. 运行TensorFlow
  10. [USACO19FEB]Cow Dating——找规律
  11. spurious wakeup -- 多线程之虚假唤醒
  12. Ubuntu16.04关机后自动重启解决方案
  13. 寻找亲和数对C语言,寻找亲和数
  14. 直方图归一化因子计算公式
  15. 文石c67ml carta(firmware:1.8.2)如何全屏看pdf书籍
  16. Web前端开发 北京林业大学 CSS样式-单元作业
  17. python实现Excel多行多列的转换
  18. 基于MATLAB的图像去遮挡修复数字图像处理系统
  19. 2019度小满秋招研发编程题_数字的情绪
  20. Python如何入门?直接按这个方式玩炸弹超人小游戏,就能掌握编程

热门文章

  1. NameNode格式化——组件恢复,数据丢失
  2. wireshark抓组播数据_wireshark怎么抓包 wireshark抓包详细图文教程
  3. 轻松学python(一)
  4. 【Apache+Tomcat+Session+Memcache 高性能群集搭建】
  5. 新元宇宙奇科幻小说原创作品每周连载地球人奇游天球记第七回月球背面
  6. 组合测试法是什么 软件测试,组合测试模型方法
  7. linux文件系统程序设计实验报告,浙江大学Linux程序设计实验报告
  8. Python3自然语言(NLTK)——语言大数据
  9. 100个python算法超详细讲解:掷骰子
  10. 大数据处理应遵循的四大原则