我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。

这次项目的需求:

支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,ie8,ie9,Chrome,Firefox,360安全浏览器,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传

支持文件夹的上传,要求服务端能够保留层级结构,并且能够续传。需要支持10万个以上的文件夹上传。

支持低版本的系统和浏览器,因为这个项目的最终运行环境在政府,政府的配置都一般,职员都是办公用,内存都不大,基本上以Windows XP的系统为主。

1、介绍enctype

enctype 属性规定发送到服务器之前应该如何对表单数据进行编码。

enctype作用是告知服务器请求正文的MIME类型(请求消息头content-type的作用一样)

1、1 enctype的取值有三种

描述

application/x-www-form-urlencoded

在发送前编码所有字符(默认)

multipart/form-data

不对字符编码。每一个表单项分割为一个部件

text/plain

空格转换为 “+” 加号,但不对特殊字符编码。

1. 当enctype=’application/x-www-form-urlencoded’

2.当enctype=’multipart/form-data’

通过观察发现这个的请求体就发生了变化。这种请求体被称之为多部件请求体。

什么是多部件请求体:就是把每一个表单项分割为一个部件。

以请求头的content-type的boundary后面的一串随机字符串作为分割标识

普通表单项:

//name的意思是文本框里面name的属性值,而admin是我们输入的文本值

Content-Disposition: form-data; name="username"

admin

文件表单项

//filename的意思是:我们上传的文件名称,content-Type的意思是:MIME类型,asdasdas的意思是:文件里面的内容

Content-Disposition: form-data; name="upload"; filename="a.txt"

Content-Type: text/plain

asdasdas

3. 当enctype=’text/plain’

w3c称:空格会变成”+”加号,但是我这里没有发现,只有当get请求的时候,空格会变成”+”号

进入正题

完成上传需要满足3个必要的条件

提供form表单,method必须是post,因为get请求的传输数据一般为2kb,不同浏览器不一样。

form表单属性enctype的必须是multipart/form-data

提供input type=”file”类的上传输入域

大致实现原理:当enctype的值是multipart/form-data时,浏览器会把每个表单项进行分割,分割成不同的部件,以boundary的值为分割标识,这个标识的字符串是随机生成的,最后一个表单项的分割标识字符串末尾会多两个”- -“,代表结束。服务端用request.getHeader(“content-type”)获取分割字符串,然后进行解析。

代码实现

一、开发环境搭建

准备两个第三方jar包

commons-io包

commons-upload包

所有依赖包

代码实现

<%@page language="java" import="up6.DBFile" pageEncoding="UTF-8"%>

<%@page contentType="text/html;charset=UTF-8"%>

<%@page import="up6.FileBlockWriter" %>

<%@page import="up6.XDebug" %>

<%@page import="up6.*" %>

<%@page import="up6.biz.*" %>

<%@page import="org.apache.commons.fileupload.FileItem" %>

<%@page import="org.apache.commons.fileupload.FileItemFactory" %>

<%@page import="org.apache.commons.fileupload.FileUploadException" %>

<%@page import="org.apache.commons.fileupload.disk.DiskFileItemFactory" %>

<%@page import="org.apache.commons.fileupload.servlet.ServletFileUpload" %>

<%@page import="org.apache.commons.lang.*" %>

<%@page import="java.net.URLDecoder"%>

<%@page import="java.util.Iterator"%>

<%@page import="net.sf.json.JSONObject"%>

<%@page import="java.util.List"%>

<%

out.clear();

String uid             = request.getHeader("uid");//

String id              = request.getHeader("id");

String lenSvr      = request.getHeader("lenSvr");

String lenLoc      = request.getHeader("lenLoc");

String blockOffset= request.getHeader("blockOffset");

String blockSize   = request.getHeader("blockSize");

String blockIndex  = request.getHeader("blockIndex");

String blockMd5        = request.getHeader("blockMd5");

String complete        = request.getHeader("complete");

String pathSvr         = "";

//参数为空

if(  StringUtils.isBlank( uid )

|| StringUtils.isBlank( id )

|| StringUtils.isBlank( blockOffset ))

{

XDebug.Output("param is null");return;

}

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

FileItemFactory factory = new DiskFileItemFactory();

ServletFileUpload upload = new ServletFileUpload(factory);

List files = null;

try {files = upload.parseRequest(request);}

catch (FileUploadException e)

{out.println("read file data error:" + e.toString());return;}

FileItem rangeFile = null;

Iterator fileItr = files.iterator();

while (fileItr.hasNext())

{

rangeFile = (FileItem) fileItr.next();

if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr"))

{

pathSvr = rangeFile.getString();

pathSvr = PathTool.url_decode(pathSvr);

}

}

boolean verify = false;

String msg = "";

String md5Svr = "";

long blockSizeSvr = rangeFile.getSize();

if(!StringUtils.isBlank(blockMd5)){md5Svr = Md5Tool.fileToMD5(rangeFile.getInputStream());}

verify = Integer.parseInt(blockSize) == blockSizeSvr;

if(!verify){ msg = "block size error sizeSvr:" + blockSizeSvr + "sizeLoc:" + blockSize;}

if(verify && !StringUtils.isBlank(blockMd5))

{

verify = md5Svr.equals(blockMd5); if(!verify) msg = "block md5 error";

}

if(verify)

{

FileBlockWriter res = new FileBlockWriter();

if( Integer.parseInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parseLong(lenLoc));

res.write( Long.parseLong(blockOffset),pathSvr,rangeFile);

up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex));

JSONObject o = new JSONObject();

o.put("msg", "ok");

o.put("md5", md5Svr);

o.put("offset", blockOffset);

msg = o.toString();

}

rangeFile.delete();

out.write(msg);

%>

下载的必须条件

两个头一个流

content-type

Content-Type是返回消息中非常重要的内容,表示文档内容属于什么MIME类型。

浏览器会根据Content-Type来决定如何显示返回的消息体内容。

默认值是text/html

可以使用request.getServletContext().getMimeType(“文件名”)获取MIME类型。

Content-Disposition

Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。

默认值是inline,表示在浏览器窗口中打开。

服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器 中显示。

如果需要提示用户保存,利用Content-Disposition进行一下处理,关键在于一定要加上attachment。

例如:Content-Disposition:attachment;filename=xxx,浏览器就会激活下载框对话框, attachment 表示附件, filname 后面跟随的是显示在下载框中的文件名称。

下载就是向客户端响应字节数据! 将一个文件变成字节数组, 使用 response.getOutputStream()

来响应给浏览器。

代码如下,此代码已经实现了断点续传功能,用户在下载过程可以暂停,和继续下载,对服务器造成的压力也比较小。

String fid             = request.getHeader("id");

String blockIndex = request.getHeader("blockIndex");//基于1

String blockOffset     = request.getHeader("blockOffset");//块偏移,相对于整个文件

String blockSize   = request.getHeader("blockSize");//块大小(当前需要下载的大小)

String pathSvr         = request.getHeader("pathSvr");//文件在服务器的位置

pathSvr            = PathTool.url_decode(pathSvr);

if (  StringUtils.isBlank(fid)

||StringUtils.isBlank(blockIndex)

||StringUtils.isEmpty(blockOffset)

||StringUtils.isBlank(blockSize)

||StringUtils.isBlank(pathSvr))

{

response.setStatus(500);

response.setHeader("err","参数为空");

return;

}

File f = new File(pathSvr);

//文件不存在

if(!f.exists())

{

response.setStatus(500);

OutputStream os = response.getOutputStream();

System.out.println(String.format("%s 文件不存在",pathSvr));

os.close();

return;

}

long fileLen = f.length();

response.setContentType("application/x-download");

response.setHeader("Pragma","No-cache");

response.setHeader("Cache-Control","no-cache");

response.addHeader("Content-Length",blockSize);

response.setDateHeader("Expires", 0);

OutputStream os = response.getOutputStream();

try

{

RandomAccessFile raf = new RandomAccessFile(pathSvr,"r");

int readToLen = Integer.parseInt(blockSize);

int readLen = 0;

raf.seek( Long.parseLong(blockOffset) );//定位索引

byte[] data = newbyte[1048576];

while( readToLen > 0 )

{

readLen = raf.read(data,0,Math.min(1048576,readToLen) );

readToLen -= readLen;

os.write(data, 0, readLen);

}

os.flush();

os.close();

raf.close();

os = null;

response.flushBuffer();

out.clear();

out = pageContext.pushBody();

}

catch(Exception e)

{

response.setStatus(500);

os.close();

out.close();

e.printStackTrace();

}

finally

{

if(os != null)

{

os.close();

os = null;

}

out.clear();

out = pageContext.pushBody();

}%>

加载文件列表,在下载列表中显示出来

后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/

java+批量下载大文件的更多相关文章

  1. JAVA 批量下载服务器文件到本地指定文件夹并重命名

    /** * @功能 下载文件到指定文件夹并重命名 * @param url 请求的路径 * @param filePath 文件将要保存的目录 * @param filename 保存到本地的文件名 ...

  2. Android 开发工具类 27_多线程下载大文件

    多线程下载大文件时序图 FileDownloader.java package com.wangjialin.internet.service.downloader; import java.io.F ...

  3. java批量下载

    最近做了一些有关批量压缩下载的功能,网上也找了一些资源,但都不是太全面,所以自己整理一份,已备不时之需. 直接上代码: // 获取项目路径     private static String WEBC ...

  4. 转(Response.WriteFile 无法下载大文件解决方法)

    以前用Response.WriteFile(filename),但当遇到大文件时无法完整下载. 该方法最大的问题,它不是直接将数据抛到客户端,而是在服务器端(IIS)上缓存.当下载文件比较大时,服务器 ...

  5. JAVA多线程下载网络文件

    JAVA多线程下载网络文件,开启多个线程,同时下载网络文件.   源码如下:(点击下载 MultiThreadDownload.java) import java.io.InputStream; im ...

  6. python下载大文件

    1. wget def download_big_file_with_wget(url, target_file_name): """ 使用wget下载大文件 Note: ...

  7. Python3 根据m3u8下载视频,批量下载ts文件并且合并

    Python3 根据m3u8下载视频,批量下载ts文件并且合并 m3u8是苹果公司推出一种视频播放标准,是一种文件检索格式,将视频切割成一小段一小段的ts格式的视频文件,然后存在服务器中(现在为了减少 ...

  8. python 下载大文件

    当使用requests的get下载大文件/数据时,建议使用使用stream模式. 当把get函数的stream参数设置成False时,它会立即开始下载文件并放到内存中,如果文件过大,有可能导致内存不足 ...

  9. java批量解压文件夹下的所有压缩文件(.rar、.zip、.gz、.tar.gz)

    // java批量解压文件夹下的所有压缩文件(.rar..zip..gz..tar.gz) 新建工具类: package com.mobile.utils; import com.github.jun ...

随机推荐

  1. C语言写郑州大学校友通讯录

    #include <stdio.h> #include <string.h> #include <stdlib.h> #define LEN sizeof(stru ...

  2. 【原创】大叔问题定位分享(34)Spring的RestTemplate请求json数据后内容被修改

    先看代码 org.springframework.web.client.RestTemplate public RestTemplate() { this.messageConverters = ne ...

  3. O034、 Nova Pause / Resume Instance 操作详解

    参考https://www.cnblogs.com/CloudMan6/p/5496825.html   本节通过日志详细分析 Nova Pause / Resume 操作.   有时需要短时间暂停  ...

  4. spring AOP的相关术语

    连接点:Joinpoint   其实业务层接口的方法 切入点:Pointcut 被增强的是切入点,没被增强是永远都是连接点.连接点不一定是切入点,切入点一定是连接点 通知:Advice 就是指要增强的 ...

  5. 防止表单提交时刷新页面-阻止form表单的默认提交行为

    最近在写 ajax 提交的时候遇到一个问题,在执行 ajax 提交之后,浏览器页面自动刷新了,主要是没有 由于form 表单的默认提交行为.一下是几种阻止 form 表单默认提交行为的方式. 1.使用 ...

  6. js之数据类型(对象类型——构造器对象——数组1)

    数组是值的有序集合,每个值叫做一个元素,而每一个元素在数组中有一个位置,以数字表示,称为索引.JavaScript数组是无类型的,数组元素可以是任意类型且同一个数组中不同元素也可能有不同的类型.数组的 ...

  7. css中animation和@keyframes 动画

    Animation 使用简写属性,将动画与 div 元素绑定: div { animation:mymove 5s infinite; -webkit-animation:mymove 5s infi ...

  8. Golang Gateway API 搭建教程

    原文链接 随着微服务的兴起,行业里出现了非常多优秀的微服务网关框架,今天教大家搭建一套国人,用Golang写的微服务网关框架. 这里啰嗦一句,可能到今天还有人不理解什么是微服务,为什么要用微服务.目前 ...

  9. docker使用上的错误

    docker启动问题 Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon ...

  10. 在STM32上如果不使用外部晶振,OSC_IN和OSC_OUT的接法

    如果使用内部RC振荡器而不使用外部晶振,请按照下面方法处理: 1)对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空.2)对于少于100脚的产品,有2种接法:  2.1)OSC_I ...