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

这次项目的需求:

支持大文件的上传和续传,要求续传支持所有浏览器,包括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/

javaweb+上传大文件的更多相关文章

  1. javaweb上传大文件的问题

    总结一下大文件分片上传和断点续传的问题.因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况.http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件 ...

  2. [Asp.net]Uploadify上传大文件,Http error 404 解决方案

    引言 之前使用Uploadify做了一个上传图片并预览的功能,今天在项目中,要使用该插件上传大文件.之前弄过上传图片的demo,就使用该demo进行测试.可以查看我的这篇文章:[Asp.net]Upl ...

  3. php 上传大文件配置upload_max_filesize和post_max_size选项

    php 上传大文件配置upload_max_filesize和post_max_size选项 (2014-04-29 14:42:11) 转载▼ 标签: php.ini upload _files[f ...

  4. PHP上传大文件 分割文件上传

    最近遇到这么个情况,需要将一些大的文件上传到服务器,我现在拥有的权限是只能在一个网页版的文件管理系统来进行操作,可以解压,可以压缩,当然也可以用它来在线编辑.php文件. 文件有40M左右,但是服务器 ...

  5. ASP.NET上传大文件的问题

    原文:http://www.cnblogs.com/wolf-sun/p/3657241.html?utm_source=tuicool&utm_medium=referral 引言 之前使用 ...

  6. php 上传大文件主要涉及配置upload_max_filesize和post_max_size两个选项

    php 上传大文件主要涉及配置 upload_max_filesize 和post_max_size两个选项   今天在做上传的时候出现一个非常怪的问题,有时候表单提交可以获取到值,有时候就获取不到了 ...

  7. SWFUpload上传大文件(暂时用用,真正用的时候还是要改的)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. PHP上传大文件和处理大数据

    1. 上传大文件 /* 以1.5M/秒的速度写入文件,防止一次过写入文件过大导致服务器出错(chy/20150327) */ $is_large_file = false; if( strlen($x ...

  9. QQ上传大文件为什么这么快

    今天和同事在群里讨论“QQ上传大文件/QQ群发送大文件时,可以在极短的时间内完成”是如何做到的. 有时候我们通过QQ上传一个几百M的文件,竟然只用了几秒钟,从带宽上限制可以得出,实际上传文件是不可能的 ...

随机推荐

  1. 机器学习xgboost参数解释笔记

    首先xgboost有两种接口,xgboost自带API和Scikit-Learn的API,具体用法有细微的差别但不大. 在运行 XGBoost 之前, 我们必须设置三种类型的参数: (常规参数)gen ...

  2. Fiddler讲解3

    想要 浏览更多Fiddler内容:请点击进入Fiddler官方文档 阅读目录: 一.Fiddler自定义请求: 二.Fiddler修改请求: 三.减少期望的延迟:100个继续标题: 四.重命名无效的P ...

  3. Scratch编程:初识Scratch及编程工具安装(一)

    “ Scratch是一款由美国麻省理工学院(MIT)设计开发的少儿编程工具.” Scratch采用可视化.模块化的编程方式,非常适合青少年作为初次接触编程的工具和语言来学习,进而用其编写充满趣味的小程 ...

  4. Pyhton模块和包

    一 模块 1.1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用pytho ...

  5. 前端相关UED团队和个人博客整理

    平时收集的UED的团队和个人博客一些有关/*********************************这次真的是搬运工,原文转载自蓝色理想********************/ 前端团队推荐 ...

  6. zookeeper安装简要步骤

    vi zoo.cfg1.dataDir=/var/zookeeper2.server.1=zoo1:2888:3888server.2=zoo2:2888:3888server.3=zoo3:2888 ...

  7. py map reduce filter 总结

    array=[1,3,4,71,2] ret=[] for i in array: ret.append(i**2) print(ret) #如果我们有一万个列表,那么你只能把上面的逻辑定义成函数 d ...

  8. CSS之简介及引入方式

    一.css的来源 1994年哈坤·利提出了CSS的最初建议.而当时伯特·波斯(Bert Bos)正在设计一个名为Argo的浏览器,于是他们决定一起设计CSS.其实当时在互联网界已经有过一些统一样式表语 ...

  9. C++——虚函数表解析

     转自:https://blog.csdn.net/haoel/article/details/1948051 前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型指针指 ...

  10. C#解析多层Json数据

    [事件回顾] 今天在做一个小项目的时候,想获取一下位置,IP地址,然后随便在网上找了一个api调用 https://apis.map.qq.com/ws/location/v1/ip 数据如下: 如果 ...