Java实现HTTP的上传与下载
相信很多人对于java文件下载的过程都存在一些疑惑,比如下载上传文件会不会占用vm内存,上传/下载大文件会不会导致oom。下面从字节流的角度看下载/上传的实现,可以更加深入理解文件的上传和下载功能。
文件下载
首先明确,文件下载不仅仅只有下载方,还有服务端也就是返回文件的服务器
那么看一个简易文件服务器返回下载的文件。
服务端
这里是使用springMvc实现
@GetMapping("download")public void downFile(HttpServletResponse response) throws IOException {response.setContentType("application/octet-stream");response.setHeader("Content-Disposition", "attachment; filename=" + "test.jhprof");File file = new File("D:\\heap\\heapDump.hprof");InputStream in = new FileInputStream(file);OutputStream out = response.getOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = in.read(buffer)) > 0) {out.write(buffer, 0, len);}in.close();out.close();}
这里每次从文件流中读取1024个字节输出,是因为如果读取太多字节会给内存造成压力,我们这里使用的是java的堆内存。如果直接完整读取整个文件,那么可以导致oom。
java.lang.OutOfMemoryError: Java heap space
客户端
URL url = new URL("http://localhost:8062/fallback/download");URLConnection conn = url.openConnection();InputStream in = conn.getInputStream();FileOutputStream fileOutputStream = new FileOutputStream("D:\\tmp\\a.hrpof");byte[] buffer = new byte[1024];int len = 0;while ((len = in.read(buffer)) > 0) {fileOutputStream.write(buffer, 0, len);}in.close();fileOutputStream.close();
同样,在下载文件时也需要注意,不要一次读取特别多的字节数
测试过程发现由于TCP发送缓冲区和接受缓冲区有限,当缓冲区满之后就会阻塞,例如下载方的速度满,服务端的文件不断写到缓冲区,缓冲区满了,就无法继续写入,那么就会导致在执行write方法时暂时阻塞。等到接收端接受到数据了,才能继续写入。
文件上传
服务端
http是支持多个文件进行上传的,文件数据都在请求体中,多个文件之间可以通过分隔符区分
例如上传两个文本文件
请求大概长这样
1.HTTP上传method=post,enctype=multipart/form-data;
2.计算出所有上传文件的总的字节数作为Content-Length的值
3.设置
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryJ9RUA0QCk13RaoAp
4.多个文件数据:请求体
------WebKitFormBoundaryJ9RUA0QCk13RaoAp
Content-Disposition: form-data; name="pic"; filename="thunder.gif"
Content-Type: image/gif
这中间是文件的二进制数据------WebKitFormBoundaryJ9RUA0QCk13RaoAp
Content-Disposition: form-data; name="pic"; filename="uuu.gif"
Content-Type: image/gif
这中间是文件的二进制数据
------WebKitFormBoundaryJ9RUA0QCk13RaoAp--
服务端使用springMvc接收上传文件的写法(多个文件)
@RequestMapping(method = RequestMethod.POST,value = "/uploadModel")public void uploadModel(@RequestPart(value = "file", required = true) List<MultipartFile> file, @RequestParam Integer type) {··········}
这样就可以直接获取到上传的文件。
具体接受过程还要看springMvc的实现
在doDispatch方法中会是否按照文件进行处理
判断方式也很简单检查请求头的multipart
@Overridepublic boolean isMultipart(HttpServletRequest request) {return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");}
如果是文件类型,那么就要通过IO流将文件下载到本地
springMvc的大致实现如下
原代码位置org.apache.tomcat.util.http.fileupload.FileUploadBase#parseRequest
FileItemIterator iter = getItemIterator(ctx);FileItemFactory fileItemFactory = Objects.requireNonNull(getFileItemFactory(), "No FileItemFactory has been set.");final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE];//8KB的字节数组用于读取字节流while (iter.hasNext()) {final FileItemStream item = iter.next();// Don't use getName() here to prevent an InvalidFileNameException.final String fileName = ((FileItemStreamImpl) item).getName();FileItem fileItem = fileItemFactory.createItem(item.getFieldName(), item.getContentType(),item.isFormField(), fileName);items.add(fileItem);try {Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer);} catch (FileUploadIOException e) {throw (FileUploadException) e.getCause();} catch (IOException e) {throw new IOFileUploadException(String.format("Processing of %s request failed. %s",MULTIPART_FORM_DATA, e.getMessage()), e);}final FileItemHeaders fih = item.getHeaders();fileItem.setHeaders(fih);}successful = true;return items;
根据分隔符找出上传的多个文件进行读取字节流,同时创建本地文件,写入到本地文件,这里循环通过8kb数组读取到内存,再写到文件,是为了防止文件过大造成占用内存大。
public static long copy(InputStream inputStream,OutputStream outputStream, boolean closeOutputStream,byte[] buffer)throws IOException {OutputStream out = outputStream;InputStream in = inputStream;try {long total = 0;for (;;) {int res = in.read(buffer);if (res == -1) {break;}if (res > 0) {total += res;if (out != null) {out.write(buffer, 0, res);}}}if (out != null) {if (closeOutputStream) {out.close();} else {out.flush();}out = null;}in.close();in = null;return total;} finally {IOUtils.closeQuietly(in);if (closeOutputStream) {IOUtils.closeQuietly(out);}}}
从这里我们也能看出来,http请求不并不是tomcat服务器接收进完全接收的,而是先接收请求头进行就开始进行处理了,至于后面要不要读取请求提,如何读取,就要看程序员的代码了,这也是程序员可以控制的。
客户端
文件上传的客户端逻辑比较复杂
package javaio;import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;/*** @author liuxishan 2023/9/3*/public class FileUpload {public static void main(String[] args) {String reslut = null;Map<String, File> files = new HashMap() {{put("0.png", new File("C:\\Users\\lxs\\Desktop\\0.png"));put("1.jpg", new File("C:\\Users\\lxs\\Desktop\\1.png"));put("big.herof", new File("D:\\heap\\heapDump.hprof"));}};try {String BOUNDARY = java.util.UUID.randomUUID().toString();String PREFIX = "--", LINEND = "\r\n";String MULTIPART_FROM_DATA = "multipart/form-data";String CHARSET = "UTF-8";URL uri = new URL("http://localhost:8062/fallback/uploadModel?type=1");HttpURLConnection conn = (HttpURLConnection) uri.openConnection();// conn.setChunkedStreamingMode(0);conn.setReadTimeout(100 * 1000);conn.setDoInput(true);// 允许输入conn.setDoOutput(true);// 允许输出conn.setUseCaches(false);conn.setRequestMethod("POST"); // Post方式conn.setRequestProperty("connection", "keep-alive");conn.setRequestProperty("Charsert", "UTF-8");conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY);conn.connect();// 首先组拼文本类型的参数StringBuilder sb = new StringBuilder();OutputStream outStream = conn.getOutputStream();outStream.flush();// 发送文件数据if (files != null)
// for (Map.Entry<String, File> file : files.entrySet()) {for (String key : files.keySet()) {StringBuilder sb1 = new StringBuilder();sb1.append(PREFIX);sb1.append(BOUNDARY);sb1.append(LINEND);sb1.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + key + "\"" + LINEND);sb1.append("Content-Type: multipart/form-data; charset=" + CHARSET + LINEND);sb1.append(LINEND);outStream.write(sb1.toString().getBytes());File valuefile = files.get(key);InputStream is = new FileInputStream(valuefile);byte[] buffer = new byte[1024];int len = 0;while ((len = is.read(buffer)) != -1) {outStream.write(buffer, 0, len);}is.close();outStream.write(LINEND.getBytes());}// 请求结束标志byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();outStream.write(end_data);outStream.flush();// 得到响应码
// success = conn.getResponseCode()==200;InputStream in = conn.getInputStream();InputStreamReader isReader = new InputStreamReader(in);BufferedReader bufReader = new BufferedReader(isReader);String line = null;reslut = "";while ((line = bufReader.readLine()) != null)reslut += line;outStream.close();conn.disconnect();} catch (Exception e) {e.printStackTrace();}}}
这里需要注意的是在使用HttpURLConnection 上传大文件时,出现内存溢出的错误,这让我产生了错觉,输入和输出流咋会暂用内存,不就是一个数据传送的管道么,都没有把数据读取到内存中,为撒会报错。。。然后就纠结了。。。
不过实在与原来的经验相违背,然后写了一个示例直接从file中读出然后写入到输出流中,发现并没有问题
查HttpURLConnection api发现其有缓存机制,数据并没有实时发送到网络,而是先缓存再发送,导致内存溢出。
解决办法:
httpConnection.setChunkedStreamingMode(0);
//不使用HttpURLConnection的缓存机制,直接将流提交到服务器上。
需要注意的是我们经常使用的hutool的http也存在这个问题
如果不指定chunkedStreamingMode也会出现oom的问题
对于大文件可以这么进行指定。
HttpResponse response = HttpUtil.createPost("http://localhost:8062/fallback/uploadModel?type=1").setChunkedStreamingMode(0).form("file", new File("D:\\heap\\heapDump.hprof")).execute();
相关文章:
Java实现HTTP的上传与下载
相信很多人对于java文件下载的过程都存在一些疑惑,比如下载上传文件会不会占用vm内存,上传/下载大文件会不会导致oom。下面从字节流的角度看下载/上传的实现,可以更加深入理解文件的上传和下载功能。 文件下载 首先明确,文件下载…...
VPG算法
VPG算法 前言 首先来看经典的策略梯度REINFORCE算法: 在REINFORCE中,每次采集一个episode的轨迹,计算每一步动作的回报 G t G_t Gt,与动作概率对数相乘,作为误差反向传播,有以下几个特点: …...
docker 笔记5:redis 集群分布式存储案例
尚硅谷Docker实战教程(docker教程天花板)_哔哩哔哩_bilibili 目录 1.cluster(集群)模式-docker版哈希槽分区进行亿级数据存储 1.1面试题 1.1.1 方案1 哈希取余分区 1.1.2 方案2 一致性哈希算法分区 原理 优点 一致性哈希算法的容错性 一致性…...
【Vue2】 axios库
网络请求库-axios库 认识Axios库为什么选择Axios库安装Axios axios发送请求常见的配置选项简单请求可以给Axios设置公共的基础配置发送多个请求 axios创建实例为什么要创建axios的实例 axios的拦截器请求拦截器响应拦截器 axios请求封装 认识Axios库 为什么选择Axios库 在游览…...
云计算 - 百度AIStudio使用小结
云计算 - 百度AIStudio使用小结 前言 本文以ffmpeg处理视频为例,小结一下AI Studio的使用体验及一些避坑技巧。 算力获得 免费的算力获得方式为:每日登录后运行一个项目(只需要点击运行,不需要真正运行)即可获得8小…...
刷新你对Redis持久化的认知
认识持久化 redis是一个内存数据库,数据存储到内存中。而内存的数据是不持久的,要想做到持久化,就需要让redis把数据存储到硬盘上。因此redis既要在内存上存储一份数据,还要在硬盘上存储一份数据。这样这两份数据在理论上是完全相…...
Greenplum-最佳实践小结
注:本文翻译自https://docs.vmware.com/en/VMware-Greenplum/7/greenplum-database/best_practices-logfiles.html 数据模型 Greenplum数据库是一个分析型MPP无共享数据库。该模型与高度规范化/事务性的SMP数据库明显不同。Greenplum数据库使用适合MPP分析处理的非…...
从Gamma空间改为Linear空间会导致性能下降吗
1)从Gamma空间改为Linear空间会导致性能下降吗 2)如何处理没有使用Unity Ads却收到了GooglePlay平台的警告 3)C#端如何处理xLua在执行DoString时候死循环 4)Texture2DArray相关 这是第350篇UWA技术知识分享的推送,精选…...
双轨制的发展,弊端和前景
双轨制是一种经济体制,指两种不同的规则或机制并行运行,以适应不同的市场或客户需求。双轨制最早出现在中国的改革开放中,是从计划经济向市场经济过渡的一种渐进式改革方式。 双轨制的发展可以分为三个阶段: 第一阶段(…...
生成对抗网络(GAN):在图像生成和修复中的应用
文章目录 什么是生成对抗网络(GAN)?GAN在图像生成中的应用图像生成风格迁移 GAN在图像修复中的应用图像修复 拓展应用领域总结 🎉欢迎来到AIGC人工智能专栏~生成对抗网络(GAN):在图像生成和修复…...
扬杰科技携手企企通,召开SRM采购供应链协同系统项目启动会
近日,中国功率半导体领先企业扬州扬杰电子科技股份有限公司(以下简称“扬杰科技”)与企企通召开SRM采购供应链协同系统项目启动会,双方项目团队成员一同出席本次会议。 会上,双方就扬杰科技采购供应链管理平台项目的目…...
AtCoder Beginner Contest 318
目录 A - Full Moon B - Overlapping sheets C - Blue Spring D - General Weighted Max Matching E - Sandwiches F - Octopus A - Full Moon #include<bits/stdc.h> using namespace std; const int N1e65; typedef long long ll ; const int maxv4e65; typedef …...
《Python魔法大冒险》003 两个神奇的魔法工具
魔法师:小鱼,要开始编写魔法般的Python程序,我们首先需要两个神奇的工具:Python解释器和代码编辑器。 小鱼:这两个工具是做什么的? 魔法师:你可以把Python解释器看作是一个魔法棒,只要你向它说出正确的咒语,它就会为你施展魔法。 小鱼:那这个解释器和我之前用的电…...
每日一题-动态规划(从不同类型的物品中各挑选一个,使得最后花费总和等于1000)
四种类型的物品,每一种类型物品数量都是n,先要从每种类型的物品中挑选一件,使得最后花费总和等于1000 暴力做法10000^4 看到花费总和是1000,很小且固定的数字,肯定有玄机,从这里想应该是用dp,不…...
2023-9-3 试除法判定质数
题目链接:试除法判定质数 #include <iostream>using namespace std;bool is_prime(int n) {if(n < 2) return false;for(int i 2; i < n / i; i){if(n % i 0) return false;}return true; }int main() {int n;cin >> n;while(n--){int x;cin &g…...
【Apollo学习笔记】——规划模块TASK之RULE_BASED_STOP_DECIDER
文章目录 前言RULE_BASED_STOP_DECIDER相关配置RULE_BASED_STOP_DECIDER总体流程StopOnSidePassCheckClearDoneCheckSidePassStopIsPerceptionBlockedIsClearToChangeLaneCheckSidePassStopBuildStopDecisionELSE:涉及到的一些其他函数NormalizeAngleSelfRotate CheckLaneChang…...
【SpringBoot】最基础的项目架构(SpringBoot+Mybatis-plus+lombok+knife4j+hutool)
汝之观览,吾之幸也! 从本文开始讲下项目中用到的一些框架和技术,最基本的框架使用的是SpringBoot(2.5.10)Mybatis-plus(3.5.3.2)lombok(1.18.28)knife4j(3.0.3)hutool(5.8.21),可以做到代码自动生成,满足最基本的增删查改。 一、新…...
RNN 单元:分析 GRU 方程与 LSTM,以及何时选择 RNN 而不是变压器
一、说明 深度学习往往感觉像是在雪山上找到自己的道路。拥有坚实的原则会让你对做出决定更有信心。我们都去过那里 在上一篇文章中,我们彻底介绍并检查了 LSTM 单元的各个方面。有人可能会争辩说,RNN方法已经过时了,研究它们是没有意义的。的…...
Linux音频了解
ALPHA I.MX6U 开发板支持音频,板上搭载了音频编解码芯片 WM8960,支持播放以及录音功能! 本章将会讨论如下主题内容。 ⚫ Linux 下 ALSA 框架概述; ⚫ alsa-lib 库介绍; ⚫ alsa-lib 库移植; ⚫ alsa-l…...
精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野
精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野。 刚开源就变成新星的 igl,不仅获得了 2k star,也能提高你开发游戏的效率,摆…...
阿里开源MGeo地址匹配:零基础3步搭建,开箱即用
阿里开源MGeo地址匹配:零基础3步搭建,开箱即用 1. 为什么你需要MGeo地址匹配? 地址数据混乱是每个数据工程师的噩梦。同一地点在不同系统中可能有十几种写法:"北京市海淀区中关村大街1号"、"北京海淀中关村1号&q…...
告别环境配置劝退!跨平台研发环境搭建终极指南:从零基础到工程化落地
对于每一位开发者而言,研发环境是所有代码的「第一生产车间」,是技术成长的起点。但行业内一个非常普遍的现状是:超过80%的编程新手,在入门的第一周就会栽在环境配置上。 下载超时、权限报错、版本冲突、command not found玄学问…...
ROS中tf2坐标系命名规范详解:为什么你的/world会报Invalid argument错误
ROS中tf2坐标系命名规范详解:为什么你的/world会报Invalid argument错误 在ROS机器人开发中,坐标系转换(tf2)系统是构建空间感知的核心基础设施。许多开发者第一次遇到Invalid argument "/world" passed to canTransfor…...
虚幻引擎C++实战:用TSharedPtr管理资源时90%人会犯的3个内存错误
虚幻引擎C实战:用TSharedPtr管理资源时90%人会犯的3个内存错误 在虚幻引擎的C开发中,智能指针系统是资源管理的核心工具之一。TSharedPtr作为UE提供的引用计数智能指针,其设计初衷是为了简化内存管理,但实际开发中却常常成为内存泄…...
Druid连接池minIdle和maxActive参数详解:如何避免连接池耗尽问题
Druid连接池minIdle与maxActive参数深度优化指南 1. 理解连接池的核心参数 在现代企业级应用中,数据库连接池的性能调优往往是系统稳定性的关键所在。作为阿里巴巴开源的Druid连接池,其minIdle和maxActive参数的合理配置直接影响着应用的吞吐量和响应时间…...
基于Matlab的齿轮动力学仿真探索
基于matlab的齿轮动力学仿真。 该模型考虑动态啮合角和动态间隙非线性的影响,将扭矩直接作用于齿轮,并定义已知的系统参数:齿数、模数、转动惯量、压力角、转速等,输出齿轮X、Y方向的时变位移及FFT结果,综合变形时域图…...
74HC595驱动8位数码管实战:从查找表到动态扫描的完整流程
74HC595驱动8位数码管实战:从查找表到动态扫描的完整流程 在嵌入式系统开发中,数码管显示是最基础也最考验硬件理解能力的环节之一。记得我第一次尝试用74HC595驱动数码管时,被那个"看似简单却暗藏玄机"的动态扫描原理折磨了整整三…...
从文档智能处理到自动化工作流:现代开发技能的全栈实践
从文档智能处理到自动化工作流:现代开发技能的全栈实践 【免费下载链接】skills 本仓库包含的技能展示了Claude技能系统的潜力。这些技能涵盖从创意应用到技术任务、再到企业工作流。 项目地址: https://gitcode.com/GitHub_Trending/skills3/skills 在日常开…...
孤能子视角:“智能动力学“,AI的“自指“分析
(我将【孤能子视角:“关系“通透,难转译为“实体“ - CSDN App】https://blog.csdn.net/lzmtw/article/details/159602104?sharetypeblog&shareId159602104&sharereferAPP&sharesourcelzmtw&sharefromlink的内容贴给悟空浏览器的悟空AI,…...
LangChain实战:从零构建一个联网搜索增强的RAG问答系统
1. 为什么需要联网搜索增强的RAG系统 传统的RAG(检索增强生成)系统有个致命伤——它只能回答知识库里已有的内容。想象一下,你去年精心构建了一个旅游推荐系统,但今年新开的网红景点它完全不知道,因为数据没更新。这就…...
