disgare 的博客
首页
博客
分类
标签
首页
博客
分类
标签
  • 网络

    • 计算机网络学习笔记
    • 网络安全相关
    • 域名和子网掩码
    • CORS 跨域资源共享
    • DNS、HTTP 与 HTTPS
    • Server-Sent Events (SSE)
    • WebSocket 长连接
  • 计算机基础

    • 操作系统 IO 相关知识
    • 操作系统学习笔记
    • 程序的机器级表示
    • 音频文件基础
    • 正则表达式相关概念
    • ffmpeg 的安装以及实现音频切分功能
    • Hex 和 Base64 编码
    • XML 的使用
  • 数据结构与算法

    • 动态规划算法学习笔记
    • 基于比较的排序算法的最坏情况下的最优下界为什么是O(nlogn)
    • 集合与数据结构学习笔记
    • 面试常见算法总结
    • 算法导论第二部分排序学习笔记
    • 算法导论第一部分学习笔记
  • Java

    • 对象之间的映射与转换
    • 反射学习笔记
    • 泛型相关概念
    • 关于 boolean 类型的坑
    • 如何使用 lambda 表达式实现排序
    • CompletableFuture 相关用法
    • CompletableFuture 源码浅要阅读
    • FutureTask 源码阅读
    • Guava 常用 API
    • Guava 源码阅读:Multimap 相关
    • Jackson 的各种使用
    • Java 的 Excel 相关操作
    • java 的常见性能问题分析以及出现场景
    • java 基础知识
    • JAVA 枚举的基础和原理
    • Java 图片文件上传下载处理
      • 上传
      • 下载
      • base64
      • 设置响应
    • Java 序列化
    • Java 异常
    • Java 语法糖
    • Java 中关于字符串处理的常用方法
    • Java 中强、软、弱、虚引用
    • JAVA 注解小结
    • Java Http 访问框架
    • Java Stream 的使用
    • Java8 新特性
    • netty 学习笔记
    • Scanner 的各种用法
    • Servlet 学习笔记
    • String、StringBuffer、StringBuilder 学习笔记
  • JVM

    • 虚拟机执行子系统
    • JVM 自动内存管理
    • Linux 中 JVM 常用工具以及常见问题解决思路
  • Linux

    • crontab 表达式
    • Linux 常见命令
    • Linux 文件系统
  • 中间件

    • 关于定时任务原理
    • 详解 kafka
    • ES 搜索引擎
    • flink 提交流程
    • Grape-RAG
    • Hadoop 基础原理
  • 多线程

    • 多线程基础学习笔记
    • 简单了解并发集合
    • 如何手写单例
    • 深入理解 java 多线程安全
    • 生产者消费者问题
    • 线程池作用、用法以及原理
    • AQS 组件
    • ThreadLocal 原理以及使用
  • 非关系型数据库

    • Redis 集群
    • Redis 数据结构、对象与数据库
    • Redis 学习笔记
  • 关系型数据库

    • B+ 树的插入、删除和数据页分裂机制
    • MySQL 的 binglog、redolog、undolog
    • MySQL 的记录存储结构、存储引擎与 Buffer Pool
    • MySQL 基本的特性
    • MySQL 开发规范
    • MySQL 事务与锁与 MVCC
    • MySQL 数据类型、字符集相关内容
    • MySQL 索引与索引优化
    • PostgreSQL 更新数据时 HOT优化
    • PostgreSQL 相关用法
  • Python

    • Python 基础语法
    • Python 学习
  • Spring 项目

    • Lombok 的常用注解
    • maven 小结
    • MyBatis 框架的使用
    • MyBatis 重要知识点总结
    • MybatisPlus 的使用
    • Spring 框架基础使用
    • Spring 事务相关
    • Spring IOC 的原理及源码
    • Spring AOP 的使用和原理
    • SpringBoot 的原理
    • SpringBoot 基础使用
    • SpringWeb 重要知识点
  • 分布式

    • 初步了解 docker
    • 从 ACID 到 BASE 事务处理的实现
    • 访问远程服务
    • 分布式 id
    • 分布式缓存相关问题
    • 分布式集群理论和分布式事务协议
    • 分布式架构的观测
    • 分布式一致性算法
    • 负载均衡 Load Balancing
    • 关于分布式系统 RPC 中高可用功能的实现
    • 集群间数据同步的目的
    • 三高问题下的系统优化
    • 数据库分库分表
    • 详解 Spring Cloud
    • Dubbo 基础概念
    • Gossip 协议
    • nginx 学习笔记
    • Protobuf 通信协议
    • Zookeeper 基础学习
  • 架构设计

    • 参数校验与异常处理
    • 抽象方法与设计模式
    • 代码整洁之道
    • 权限系统设计
    • 用低内存处理大量数据
    • 设计模式——策略模式
    • 设计模式——过滤器模式在 Spring 中的实践
    • 状态模式
    • 统一结果返回
    • 为什么要打日志?怎么打日志?打什么日志?
    • 运维监控常见指标含义
    • 资深研发进阶
    • DDD 架构学习笔记
    • Java 常用的规则引擎
    • MVC 架构学习笔记
  • AI

    • 如何编写 Prompt
    • Agent 工程架构
    • LLM 相关内容
    • NLP 相关知识
    • vibe coding 最佳实践
    • windows 下 ollama 迁移到 D 盘
  • 开发工具

    • 如何画时序图、流程图、状态流转图
    • excel 关于 =vlookup 的用法
    • git 的学习以及使用
    • IDEA 插件推荐
    • IDEA 常用快捷键以及调试
    • Shell 脚本
    • swagger 的使用
  • 前端

    • 简单了解前端页面开发
    • 伪静态是什么
    • GitHub Pages 部署教程
    • Vercel 部署教程
    • vue-admin-template 简单使用
    • VuePress 博客搭建指南
  • 项目

    • 面试刷题网——技术方案
    • 影视资源聚合站——技术方案
  • 问题记录

    • 定时任务单线程消费 redis 中数据导致消费能力不足
    • 提供可传递的易受攻击的依赖项
    • Liteflow 在 SpringBoot 启动时无法注入组件问题 couldn‘t find chain with the id[THEN(NodeComponent)]
  • 金融

    • 股票分析——关于电力
    • 股票技术面——量价关系
    • 股票技术面——盘口
    • 股票技术面——基础
    • 基础的金融知识
    • 基金与股票
    • 韭菜的自我总结
    • 聊聊价值投资
  • 其他

    • 程序员职场工作需要注意什么
    • 创业全链路SOP:从灵光一现到系统化增长的实战指南
    • 观罗翔讲刑法随笔
    • 价格和价值
    • 立直麻将牌效益理论
    • 梅花易数学习笔记
    • 压力管理
2023-12-18
Java
目录

Java 图片文件上传下载处理

做这玩意给我恶心坏了

# 上传

上传图片到本地的代码

    public void uploadFileToDisk(HttpServletRequest request, HttpServletResponse response, String baseDir) {
        File dir = new File(baseDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        List<FileItem> items = null;
        try {
            items = getFileItemList(request, response, dir);
        } catch (Exception e) {
            log.warn("服务器后台没有从请求中获取到上传文件的信息,异常信息:", e);
        }
    }

    public static List<FileItem> getFileItemList(HttpServletRequest request, HttpServletResponse response, File baseDir) throws UnsupportedEncodingException, FileUploadException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);

        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存缓冲区,超过后写入临时文件
        factory.setSizeThreshold(1024 * 512);
        // 设置缓存文件路径,路径为空则不设置
        if (baseDir != null) {
            factory.setRepository(baseDir);
        }
        // 设置临时文件存储位置
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置单个文件的最大上传值
        upload.setFileSizeMax(10 * 1024 * 1024);
        // 设置整个request的最大值
        upload.setSizeMax(10002400000L);
        upload.setHeaderEncoding("UTF-8");
        List<FileItem> fileItems = new ArrayList<>();

        if (request instanceof MultipartHttpServletRequest) {
            MultiValueMap<String, MultipartFile> multipartFileMultiValueMap = ((MultipartHttpServletRequest) request).getMultiFileMap();
            for (String key : multipartFileMultiValueMap.keySet()) {
                List<MultipartFile> multipartFileList = multipartFileMultiValueMap.get(key);
                if (CollectionUtils.isEmpty(multipartFileList)) {
                    continue;
                }
                for (MultipartFile multipartFile : multipartFileList) {
                    CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile)multipartFile;
                    fileItems.add(commonsMultipartFile.getFileItem());
                }
            }
        } else {
            fileItems = (List<FileItem>) upload.parseRequest(request);
        }

        return fileItems;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

# 下载

直接访问上传的路径就可以下载图片了。但是我们往往会包一层接口,以流的方式读取 url 的内容然后返回给前端,这么做的优点是:

  • 内网域名转外网域名,做业务校验并且让用户可以访问内网数据
  • 为所有的后端预览做一个统一的出口
  • 让访问的图片支持跨域

这么做的一般代码为

    void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileName = request.getParameter("file_name");
        OutputStream out = response.getOutputStream();
        String url = request.getParameter("file_path");
        byte[] bytes;
        // 读取流数据
        VacationClient2 vc = new VacationClient2(2000, 5000);
        byte[] bs = vc.getContentBytes(url);
        // 一般返回的是 base64 串,但是有可能会返回一般的字符串,不需要做 base64 解码时做个判断处理一下
        if (url.matches("^.*imgs\\.qunarzz\\.com.*\\.$")) {
            bytes = bs;
        } else {
            String newString = new String(bs);
            // 替换所有的空格到+
            newString = newString.replaceAll(" ", "+");
            bytes = new BASE64Decoder().decodeBuffer(newString);
        }
        downloadFileUnderHttps(request, response, fileName, bytes);
        out.write(bytes);
        out.flush();
        out.close();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# base64

讲一下 base64 是什么:

  • Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法,由于 2^6=64,所以每 6 个比特为一个单元,对应某个可打印字符
  • Base64 常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括 MIME 的电子邮件及 XML 的一些复杂数据
  • Base64 编码要求把 3 个 8 位字节(38=24)转化为 4 个 6 位的字节(46=24),之后在 6 位的前面补两个 0,形成 8 位一个字节的形式。 如果剩下的字符不足 3 个字节,则用 0 填充
  • Base64 编码后的输出还是按照 UTF-8 来输出

为什么要使用 Base64?

  • 存储二进制数据:在某些情况下,需要将二进制数据存储到文本文件中,例如将图片或音频文件存储到数据库或文本文件中。由于文本文件只能存储文本数据,无法直接存储二进制数据,因此可以将二进制数据转换为 Base64 编码的文本,然后存储到文本文件中
  • URL传参:在 URL 中传递参数时,某些字符可能会被 URL 编码,或者传的参数被误解为 url 的连接符,导致传递的参数变得很长。通过将参数进行 Base64 编码,可以将参数转换为可打印的 ASCII 字符,减少URL长度。

总之,使用 Base64 编码可以方便地将二进制数据转换为可打印的 ASCII 字符,以便在网络传输或存储到文本文件中

为了保证所输出的编码位可读字符,Base64 制定了一个编码表,以便进行统一转换。编码表的大小为 2^6=64,这也是 Base64 名称的由来 在这里插入图片描述

在 Base64 中的可打印字符包括字母 A-Z、a-z、数字 0-9,这样共有 62 个字符,此外两个可打印符号在不同的系统中而不同

以下是 Base64 编码的基本步骤:

  • 将数据划分为 3 个字节一组(24位)
  • 将每个字节转换为 8 位二进制形式
  • 将 24 位数据按照 6 位一组进行划分,得到 4 个 6 位的组
  • 将每个 6 位的组转换为对应的 Base64 字符
  • 如果数据不足 3 字节,进行填充
  • 将所有转换后的 Base64 字符连接起来,形成最终的编码结果
  • 解码 Base64 编码的过程与编码相反,将每个 Base64 字符转换为对应的6位二进制值,然后将这些 6 位值组合成原始的二进制数据

Base64 编码具有以下特点:

  • 编码后的数据长度总是比原始数据长约 1/3
  • 编码后的数据可以包含 A-Z、a-z、0-9 和两个额外字符的任意组合
  • Base64 编码是一种可逆的编码方式,可以通过解码还原原始数据

编码 Man 的结果为 TWFu,转换过程如下: 在这里插入图片描述

此外还有一个问题,Base64 加密后的数据通过 http 传输后,后台接收到的数据可能会出现空格的问题。这个问题还不知道具体原因,可能原因如下:

  • 加号(+):url 编码后,会用 %2 替换原来位置的 +。这里 url 的编码规则是符号的 Unicode 值前面加一个 %。因此 + 对应的是 %2B,空格则是 %20
  • form 表单提交,默认是 application/x-www-form-urlencoded,因此会对参数进行 urlencode

# 设置响应

最后,贴一下响应头的代码:

    public static void downloadFileUnderHttps(HttpServletRequest request,
                                              HttpServletResponse response,
                                              String filename, byte[] fileBytes) throws UnsupportedEncodingException {
        String agent = request.getHeader("User-Agent");
        filename = filename.replaceAll("filename=", "");

        if (agent != null && agent.indexOf("Windows") != -1) {
            filename = new String(filename.getBytes("GB2312"), "ISO_8859_1");
        } else {
            filename = new String(filename.getBytes("UTF-8"), "ISO_8859_1");
        }
        // 通过文件开头获取文件类型
        String contentType = FileTypeUtil.getFileContentType(fileBytes);

        if (StringUtils.isNotBlank(contentType)) {
            response.setContentType(contentType);
            response.addHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
        } else {
            response.setContentType("APPLICATION/OCTET-STREAM");
            response.addHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
            response.setHeader("Content-Transfer-Encoding", "binary");
        }
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Pragma", "public");
        response.setHeader("Cache-Control", "public");
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

常见的文件开头如下:

  • JPEG (jpg),文件头:FFD8FFE1
  • PNG (png),文件头:89504E47
  • GIF (gif),文件头:47494638
  • TIFF (tif),文件头:49492A00
  • Windows Bitmap (bmp),文件头:424D
  • CAD (dwg),文件头:41433130
  • Adobe Photoshop (psd),文件头:38425053
  • Rich Text Format (rtf),文件头:7B5C727466
  • XML (xml),文件头:3C3F786D6C
  • HTML (html),文件头:68746D6C3E
#图片文件#上传#下载
最后更新: 2/23/2026, 9:23:04 AM
JAVA 枚举的基础和原理
Java 序列化

← JAVA 枚举的基础和原理 Java 序列化→

最近更新
01
vibe coding 最佳实践
02-24
02
立直麻将牌效益理论
02-23
03
伪静态是什么
02-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式