web uploader 上传大文件总结
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数
下面直接贴代码吧,一些难懂的我大部分都加上注释了:
上传文件实体类:
看得出来,实体类中已经有很多我们需要的功能了,还有实用的属性。如MD5秒传的信息。
publicclassFileInf {
public FileInf(){}
publicStringid="";
publicStringpid="";
publicStringpidRoot="";
/** * 表示当前项是否是一个文件夹项。 */
publicbooleanfdTask=false;
// /// 是否是文件夹中的子文件 ///
publicbooleanfdChild=false;
/** * 用户ID。与第三方系统整合使用。 */
publicintuid=0;
/** * 文件在本地电脑中的名称 */
publicStringnameLoc="";
/** * 文件在服务器中的名称。 */
publicStringnameSvr="";
/** * 文件在本地电脑中的完整路径。示例:D:\Soft\QQ2012.exe */
publicStringpathLoc="";
/** * 文件在服务器中的完整路径。示例:F:\\ftp\\uer\\md5.exe */
publicStringpathSvr="";
/** * 文件在服务器中的相对路径。示例:/www/web/upload/md5.exe */
publicStringpathRel="";
/** * 文件MD5 */
publicStringmd5="";
/** * 数字化的文件长度。以字节为单位,示例:120125 */
publiclonglenLoc=0;
/** * 格式化的文件尺寸。示例:10.03MB */
publicStringsizeLoc="";
/** * 文件续传位置。 */
publiclongoffset=0;
/** * 已上传大小。以字节为单位 */
publiclonglenSvr=0;
/** * 已上传百分比。示例:10% */
publicStringperSvr="0%";
publicbooleancomplete=false;
publicDatePostedTime = newDate();
publicbooleandeleted=false;
/** * 是否已经扫描完毕,提供给大型文件夹使用,大型文件夹上传完毕后开始扫描。 */
publicbooleanscaned=false;
}
首先是文件数据接收逻辑,负责接收控件上传的文件块数据,然后写到服务器的文件中。控件已经提供了块的索引,大小,MD5和长度信息,我们可以根据需要来灵活进行处理,也可以将文件块的数据保存到分布式存储系统中。
<%
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;
}
// Check that we have a file upload request
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);
%>
文件初始化部分
<%
out.clear();
WebBase web = new WebBase(pageContext);
String id = web.queryString("id");
String md5 = web.queryString("md5");
String uid = web.queryString("uid");
String lenLoc = web.queryString("lenLoc");//数字化的文件大小。12021
String sizeLoc = web.queryString("sizeLoc");//格式化的文件大小。10MB
String callback = web.queryString("callback");
String pathLoc = web.queryString("pathLoc");
pathLoc = PathTool.url_decode(pathLoc);
//参数为空
if (StringUtils.isBlank(md5)
&& StringUtils.isBlank(uid)
&& StringUtils.isBlank(sizeLoc))
{
out.write(callback + "({\"value\":null})");
return;
}
FileInf fileSvr= new FileInf();
fileSvr.id = id;
fileSvr.fdChild = false;
fileSvr.uid = Integer.parseInt(uid);
fileSvr.nameLoc = PathTool.getName(pathLoc);
fileSvr.pathLoc = pathLoc;
fileSvr.lenLoc = Long.parseLong(lenLoc);
fileSvr.sizeLoc = sizeLoc;
fileSvr.deleted = false;
fileSvr.md5 = md5;
fileSvr.nameSvr = fileSvr.nameLoc;
//所有单个文件均以uuid/file方式存储
PathBuilderUuid pb = new PathBuilderUuid();
fileSvr.pathSvr = pb.genFile(fileSvr.uid,fileSvr);
fileSvr.pathSvr = fileSvr.pathSvr.replace("\\","/");
DBConfig cfg = new DBConfig();
DBFile db = cfg.db();
FileInf fileExist = new FileInf();
boolean exist = db.exist_file(md5,fileExist);
//数据库已存在相同文件,且有上传进度,则直接使用此信息
if(exist && fileExist.lenSvr > 1)
{
fileSvr.nameSvr = fileExist.nameSvr;
fileSvr.pathSvr = fileExist.pathSvr;
fileSvr.perSvr = fileExist.perSvr;
fileSvr.lenSvr = fileExist.lenSvr;
fileSvr.complete = fileExist.complete;
db.Add(fileSvr);
//触发事件
up6_biz_event.file_create_same(fileSvr);
}//此文件不存在
else
{
db.Add(fileSvr);
//触发事件
up6_biz_event.file_create(fileSvr);
FileBlockWriter fr = new FileBlockWriter();
fr.CreateFile(fileSvr.pathSvr,fileSvr.lenLoc);
}
Gson gson = new Gson();
String json = gson.toJson(fileSvr);
json = URLEncoder.encode(json,"UTF-8");//编码,防止中文乱码
json = json.replace("+","%20");
json = callback + "({\"value\":\"" + json + "\"})";//返回jsonp格式数据。
out.write(json);%>
第一步:获取RandomAccessFile,随机访问文件类的对象
第二步:调用RandomAccessFile的getChannel()方法,打开文件通道 FileChannel,这块逻辑可以优化,如果以后有分布式存储需求,可以改为分布式存储,减轻单台服务器的压力。
public class FileBlockWriter {
public FileBlockWriter(){}
public void CreateFile(String pathSvr,long lenLoc)
{
try
{
File ps = new File(pathSvr);
PathTool.createDirectory(ps.getParent());
RandomAccessFile raf = new RandomAccessFile(pathSvr, "rw");
raf.setLength(lenLoc);//fix:以原始大小创建文件
raf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void write(long offset,String pathSvr,FileItem block)
{
try
{
InputStream stream = block.getInputStream();
byte[] data = new byte[(int)block.getSize()];
stream.read(data);
stream.close();
RandomAccessFile raf = new RandomAccessFile(pathSvr,"rw");
raf.seek(offset);
raf.write(data);
raf.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第三步:获取当前是第几个分块,计算文件的最后偏移量
第四步:获取当前文件分块的字节数组,用于获取文件字节长度
第五步:使用文件通道FileChannel类的 map()方法创建直接字节缓冲器 MappedByteBuffer
第六步:将分块的字节数组放入到当前位置的缓冲区内 mappedByteBuffer.put(byte[] b);
第七步:释放缓冲区
第八步:检查文件是否全部完成上传
文件夹扫描类
存储路径生成类
好了,到此就全部结束了,如果有疑问或批评,欢迎评论和私信,我们一起成长一起学习。
最后放一张实现的效果图
后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/07/java超大文件上传与下载/
web uploader 上传大文件总结的更多相关文章
- web页面上传大文件有没有好的解决方案
本人在2010年时使用swfupload为核心进行文件的批量上传的解决方案.见文章:WEB版一次选择多个文件进行批量上传(swfupload)的解决方案. 本人在2013年时使用plupload为核心 ...
- WEB上传大文件解决方案
众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...
- Web上传大文件的解决方案
需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制. 第一步: 前端修改 由于项目使用的是 ...
- WEB上传大文件
众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...
- 【Web应用】JAVA网络上传大文件报500错误
问题描述 当通过 JAVA 网站上传大文件,会报 500 错误. 问题分析 因为 Azure 的 Java 网站都是基于 IIS 转发的,所以我们需要关注 IIS 的文件上传限制以及 requestT ...
- web上传大文件(>4G)有什么解决方案?
众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...
- ASP.NET 使用 plupload 上传大文件时出现“blob”文件的Bug
最近在一个ASP.NET 项目中使用了plupload来上传文件,结果几天后客户发邮件说上传的文件不对,说是文件无法打开 在进入系统进行查看后发现上传的文件竟然没有后缀,经过一番测试发现如果文件上传的 ...
- java上传大文件解决方案
需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在10G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以10G来进行限制. 第一步: 前端修改 由于项目使用的是BJ ...
- [Asp.net]Uploadify上传大文件,Http error 404 解决方案
引言 之前使用Uploadify做了一个上传图片并预览的功能,今天在项目中,要使用该插件上传大文件.之前弄过上传图片的demo,就使用该demo进行测试.可以查看我的这篇文章:[Asp.net]Upl ...
随机推荐
- 通过VLC的ActiveX进行二次开发,实现一个多媒体播放器 2011-04-10 00:57:23
http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=25498312&id=218294 通过VLC的ActiveX进行二 ...
- VS2012生成Web时报未能找到元数据文件xxx.dll
问题:引用里已经添加了,还是报‘未能找到元数据文件xxx.dll’ 解决:添加了相同的不同路径的xxx.dll文件,删掉一个用不到的,就不报错了
- 转载 IDEA搭建maven项目详细步骤(解决没有src及其下面的文件)
转载自
- C# 身份证号码验证正则和验证函数
做身份证验证的时候要求能够按照标准18位身份证验证,普通正则表达式不能满足需求,所以在网上找到了这个函数,很好用,虽然还是有漏洞,不过一般乱填的号码都能被屏蔽掉 身份证验证函数(标准18位验证) pr ...
- 解决ubuntu下eth0不显示
今天电脑重启之后,用ifconfig查看网络地址,就发现eth0神奇的消失了,顿时感觉吓尿了. 按照网上看到的资料,发现输入ifconfig -a 发现可以显示eth0,但是当输入ifconfig就没 ...
- [NOIP2016PJ]魔法阵
今天模拟赛的题,,,唯一没有Giao出来的题(不然我就AKIOI了~) 最开始没想到数学题,把所有部分分都说一遍吧: 35分:纯暴力O(M^4)枚举,对于每一组a,b,c,d验证其是否合法. 60分: ...
- Go语言_并发
并发 Go 将并发结构作为核心语言的一部分提供.本节课程通过一些示例介绍并展示了它们的用法. Go 作者组编写,Go-zh 小组翻译. https://tour.go-zh.org/concurren ...
- .net 异步
原文:https://www.cnblogs.com/wisdomqq/archive/2012/03/26/2412349.html 在说到异步前,先来理一下几个容易混淆的概念,并行.多线程.异步. ...
- differential evolution代码实例(DE算法)
DE算法是遗传算法中一种比较流行的算法,这种算法比较简单,速度也比较快,下面给出一份示例代码 clear all; close all; clc 2 %Function to be minimized ...
- C语言如何打印出%
1. 敲ASCII码,但系我记不住呀! 2. 两个%%: #include <stdio.h> int main() { printf("%%\n"); printf( ...