第一章节:自动上传非主流,IC,IP,IQ卡统统告诉我密码----上传方法总结
前段时间做一个上传模块,很是上火。要求根据解析来的文件路径自动上传。有点IC,IP,IQ卡统统告诉我密码的感觉。换言之,就是自动上传。
在这要感谢我的良师益友许老师,没有他的热情帮助这个任务直接完不成!
话说上传,不管你是asp,.net ,jsp,php你只要跟微软同学打交道。我艹,一般都是file标签,然后配合一系列的组件来上传。你可能说你是struts,俺不用file标签。老师,其实那玩意解析过了,也是对应<input type="file">标签。人家微软为了安全让你点击浏览--->选文件--->文件路径付给这个file标签的value。 这里补充说明一下,这个value是只读了。一般情况下,你赋值不好使,你可能要说通过shell,那个动态多文件上传,他就不好使了。
闲话少说,先说上传分类(传统套路少林拳)file标签+ 组件这个网上很多。咱不说了。不走寻常路的美特斯邦威说:老师有没有不用file标签滴 。天空中传来一个朦胧地声音,乾坤大挪移舶来品不用file标签,类似QQ的信箱附件添加滴那些一般都是需要客户端控件(ActiveX)类似插件,插了进来(问什么要插呢)。这个太深奥,初学者内功不是很深厚的不容易掌握(九阴真经)。
再有就是咱这个思路了ADOstream (绝对滴凌波微步)。用这个的思路大致上说一下
创建xml,将要上传的文件内容用adostream来读(字节流)插到xml的节点上。然后ajax方式发送服务器端,解析这个xml 读节点内容。写到指定路径下,写文件。
故事梗概大概到这也就结束了,咱下面来看看赠品:(struts1.2 +adostream+xml+jsp+...)
读文件内容,获得字节流放在xml,ajax发送后台
function filepost(jid,pnm)
{
//debugger;
var FileName="";
var tableobj = window.frames["iframe1"].document.getElementById("file_list");//获得iframe中表的句柄
var ado = new ActiveXObject("ADODB.Stream");//创建ADO-stream 对象
ado.Type=1; // 1=adTypeBinary ado;
// ado.Type = 2;
var xml_dom = new ActiveXObject("MSXML2.DOMDocument"); //创建包含默认头信息和根节点的 XML文档
xml_dom.loadXML('<?xml version="1.0" ?> <root/>');
xml_dom.documentElement.setAttribute("xmlns:dt", "urn:schemas-microsoft-com:datatypes");//指定数据类型
var tal="";
for( i=1; i<tableobj.rows.length; i++)
{
if(tableobj.rows[i].cells[1].innerText!="")
{
PathName = tableobj.rows[i].cells[1].innerText ;
var strArray=new Array();
strArray=PathName.split("//") ;
FileName = strArray[strArray.length-1] ;//获得文件名
var fnode=xml_dom.createElement("file");//创建file节点
xml_dom.documentElement.appendChild(fnode);
var lastnode=xml_dom.documentElement.lastChild;
fnode=xml_dom.createElement("name");//创建name节点
fnode.text=FileName
lastnode.appendChild(fnode);
fnode=xml_dom.createElement("content");//创建文件内容节点
//fnode.dataType = "string";
fnode.dataType = "bin.base64";
ado.Open();
ado.LoadFromFile(PathName);
fnode.nodeTypedValue=ado.Read(-1);//将文件内容存入XML节点
ado.Close();
lastnode.appendChild(fnode);
}
}
//alert("xml******************"+xml_dom.xml);
// alert("xml******************"+xml_dom)
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); // 把XML文档发送到Web服务器
// var post_url = "/batchSoftapp.do?method=fileUpLoad" ;
var post_url = "/batchSoftapp.do?method=fileUpLoad&currJobid="+ jid+"&projectName="+pnm;
alert("post_url======="+post_url);
xmlhttp.open("POST",post_url,false);
xmlhttp.send(xml_dom);
//xmlhttp.send(xml_dom.xml);
}
后台接收,解析部分
BatchSoftappForm batchform = (BatchSoftappForm)form;
System.out.println("文件上传执行");
System.out.println("fileUpLoad");
PrintWriter out = response.getWriter();
SAXBuilder builder = new SAXBuilder();
Document requestDoc = null;// 输入XML DOC
Document responseDoc = null;// 输出XML DOC
XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat().setEncoding("utf-8"));
String dir = this.getServlet().getServletContext().getRealPath("uploadfile")+"//"+request.getParameter("projectName")+"//"+request.getParameter("currJobid");
System.out.println("新建服务器端文件保存路径==="+dir);
if(request.getParameter("currJobid")!=null)
{
File mdkFile = new File(dir);
if(!mdkFile.exists()){
mdkFile.mkdirs();
}
}
System.out.println("写文件内容");
try {
requestDoc = builder.build(request.getInputStream());//由XML文档的输入流得到一个org.w3c.dom.Document对象
Element root = requestDoc.getRootElement();
List list = root.getChildren("file");// 取名字为file的所有元素
System.out.println("listSize : "+list.size());
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
byte[] b = new sun.misc.BASE64Decoder().decodeBuffer(e.getChildText("content")); //解码很关键
ByteArrayInputStream inStream=new ByteArrayInputStream(b);
FileOutputStream fs=new FileOutputStream(dir+"/"+e.getChildText("name"));
int bytesum=0;
int byteread=0;
while ((byteread=inStream.read(b))!=-1){
bytesum+=byteread;
fs.write(b,0,byteread);
}
System.out.println("filename=="+e.getChildText("name")+"文件保存路径=="+dir);
fs.close();
inStream.close();
}
}
后台处理xml有很多方法,咱用jdom方式。这个直接使用不用插包。
后台需要导的类有
import java.io.* ;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
import sun.misc.BASE64Decoder;//解码滴
import sun.misc.BASE64Encoder;
片场花絮:
开发时注意(防火防蚊虫)input标签,俺不管你是hidden,box,text滴,一不高兴MSDN就骂,咱浏览器发送时你可能hidden能传个9M的东西,到了web服务器,人家对post上来的值有限制,谢谢。tomcat 2M默认滴。服务器限制咱可以弄,给他弄大,可是服务器端request.get***只能得到100K左右。换言之,html Form中 标签能传值的大小是100K左右(单个滴eg<input type="text">100k <input type ="hidden">又是100K并不是这个form只能传100k)!
再有就是struts中动态标签添加的问题。比如动态上传时,我最初的思路是用hidden来存放JavaScript读取的文件内容。文件数目不定用动态添加 hidden name="a1".......ai的方式,Formbean不好操作,这时完全可以定义同名的hidden标签在FormBean中定义一个数组来存放。后台遍历下for(***)就能取到。
再有就是post get发送请求的问题。你可能整天用,但是其中的原理可能您也不一定能说的很明白。来,咱一起来给他脱,post传值对应http报文的body部分。这部分不限制你的大小,你有多少他能传多少。(类似老子看到某东西时的感叹,吞吐万物啊),get对应http报文的head部,他这块有限制。咱一般的网址请求(url)? 后边的那些,一般是1K左右,就是受这个限制地。当然你也可以调web服务器上的http报文的head 大小。来传输更大的报文头。我试过一下,调到1M左右,我艹,页面加载大约需要5分钟。你想想,5分钟啊!由此可见有的东西太大就不好使。
第二章节:无组件批量下载
再一次感谢感谢老师,在他的热情帮助还耐心讲解下,struts1.2框架的批量下载(无组件)终于新鲜出炉了。
需求:网页上复选框选择要下载的文件(类似Mp3批量下载,只不过人家的大多数是组件滴),然后下载选中的文件。
设计思路:前台jsp页面form 传参传文件路径,多文件的话,将这些文件路径用分隔符拼接,后台分割这些文件路径,然后将这些文件路径下的文件打包,然后在下载。总体来说三步走,上传文件(路径),文件打包,下载。
思路出来了,代码如下仅供参考:
要下载作业下文件目录显示:
public ActionForward JobIdFileList(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
System.out.println("zyFileList");
Userinfo userinfo =(Userinfo)request.getSession().getAttribute("userinfo");
// if(userinfo == null){
// return null;
// }
String strpath = this.getServlet().getServletContext().getRealPath("uploadfile")+"//"+request.getParameter("projectName")+"//"+request.getParameter("JobId");
System.out.println("dir============="+strpath);
//ArrayList<String> filelist = new ArrayList<String>();
Map<String,Long> formatFileMap = new HashMap<String,Long>();
File dir = new File(strpath);//通过路径来创建一个file实例
File[] files = dir.listFiles();//返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件
// System.out.println(""+files.length);
if (files==null)
System.out.println("该作业ID下文件为空") ;
for (int i=0;i<files.length;i++){
formatFileMap.put(files[i].toString(),files[i].length() );
}
request.setAttribute("fileMap", formatFileMap);
return mapping.findForward("zyFileList");
}
前台作业选择页面
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.*" %>
<html:form action="/batchSoftapp.do?method=fileDownLoad" method="post">
<div id="box_all">
<div id="box_left">
<table style="width:800px;" class="tableList" border="1" cellspacing="0" cellpadding="0" style="table-layout: fixed;" align="center" >
<tr style="background-color:#D7F2F4" align="center">
<td class="fixhead" width="8%">选择</td>
<td class="fixhead" width="75%" >文件名</td>
<td class="fixhead" width="17%" >文件大小</td>
</tr>
<%
Map map = (Map)request.getAttribute("fileMap");
//System.out.println("map.size"+map.size());
if(map!=null){
Iterator keyValuePairs = map.keySet().iterator();
while(keyValuePairs.hasNext()) {
String filepath = (String)keyValuePairs.next();
String filename =filepath.substring(filepath.lastIndexOf("//")+1);
//System.out.println();
Long filelength=(Long)map.get(filepath);
%>
<tr>
<td><input name="filechecked" id="filechecked" type="checkbox" checked= "true" align="center" value="<%=filename %>,<%=filepath %>"> </td>
<td><%=filename %> </td>
<td><%=filelength%>字节 </td>
</tr>
<% }
}
%>
</table>
</div>
<div id="box_right" align="center">
<input type="button" value="全选" title="选择下载的文件" οnclick="SelectAll(this.form)">
<input type="button" value="反选" title="反向选择下载的文件" οnclick="TurnOver(this.form)" >
<input type="button" value="下载" title="下载选中的文件" οnclick="DownLodSelected(this.form)">
<input type="button" value="返回" title="返回作业ID列表页面" οnclick="window.history.back()">
</div>
<input type="hidden" name="filenames" id="filenames" >
<input type="hidden" name="filepaths" >
</div>
</html:form>
</body>
</html:html>
<script type="text/javascript">
/**
* 功能:复选框全选
* 参数:form对象
* 思路:
* 作者: */
function SelectAll(oForm){
for(var i=0;i<oForm.filechecked.length;i++){
oForm.filechecked[i].checked=true;
}
}
/**
* 功能:复选框反选
* 参数:form对象
* 思路:
* 作者: */
function TurnOver(oForm){
for(var i=0;i<oForm.filechecked.length;i++){
oForm.filechecked[i].checked=!oForm.filechecked[i].checked;
}
}
/**
* 功能:下载文件列表实现
* 参数:form对象
* 思路:
* 作者: */
function DownLodSelected(oForm){
if(confirm("因需要在服务端动态打包,需要时间比较长,是否继续批量下载?"))
{//debugger;
var arrDownloadList = [];
for(var i=0;i<document.getElementsByName("filechecked").length;i++)
{
if(document.getElementsByName("filechecked")[i].checked==true)
{
if(arrDownloadList.length==0){
arrDownloadList[0] = document.getElementsByName("filechecked").value;
}
arrDownloadList[arrDownloadList.length] = document.getElementsByName("filechecked")[i].value;
}
}
if(arrDownloadList.length>0){
var temp=[];
var filenames="";
var filepaths="";
for(var i=1;i<arrDownloadList.length;i++){
//alert("=========="+arrDownloadList[i])
temp = arrDownloadList[i].split(",")
if(filenames==""&&filepaths==""){
filenames=temp[0];
filepaths=temp[1];
}else{
filenames=filenames+"|"+temp[0];
filepaths=filepaths+"|"+temp[1];
}
}
//alert("filenames==="+filenames);
//alert("filepaths==="+filepaths);
downloadFile(filenames,filepaths);
}else{
alert("还没有选中下载项");
}
}
}
/**
*功能: 文件下载
*思路:
*參數說明:复选框选中的要下载的文件名拼接,以及文件路径拼接
*返回值 :
*作者: */
function downloadFile(filenames,filepaths){
//location.href=encodeURI(encodeURI("/batchSoftapp.do?method=fileDownLoad&filenames="+filenames+"&filepaths="+filepaths));
document.forms[0].filenames.value=filenames;
document.forms[0].filepaths.value=filepaths;
document.forms[0].submit();
}
</script>
后台Action 部分关键部分
String filenames;
String filepaths;
String[] filenameArray = null;
String[] filepathArray = null;
String filename;
String filepath;
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
filenames=request.getParameter("filenames");
filepaths=request.getParameter("filepaths");
System.out.println("filenames==================="+filenames);
System.out.println("filepaths==================="+filepaths);
filenameArray = filenames.split("//|");//传入要进行打包的所有文件
filepathArray = filepaths.split("//|");//传入要进行打包的所有文件路径
//filename="批量打包下载.zip";
filename=format.format(new Date())+".zip";
//String requestip =ServletActionContext.getRequest().getLocalAddr();
System.out.println("*******************"+this.getServlet().getServletContext().getRealPath("/uploadfile/temp"));
//filepath = "c://批量打包下载.zip"; //产生的压缩文件路径和名字
filepath = this.getServlet().getServletContext().getRealPath("/uploadfile/temp")+"//"+filename;
File tempfile = null;
if(filenameArray!= null&&filepathArray!= null&&filenameArray.length>0 && filenameArray.length==filepathArray.length ){
try{
File f=new File(filepath);
f.createNewFile();
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f));
out.putNextEntry(new ZipEntry("/"));
for(int i=0;i<filepathArray.length;i++){
//FileInputStream In = new FileInputStream(this.getServlet().getServletContext().getRealPath(filepathArray[i]));
// System.out.println("路径"+this.getServlet().getServletContext().getRealPath(filepathArray[i]));
FileInputStream In = new FileInputStream(filepathArray[i]);
out.putNextEntry(new ZipEntry(filenameArray[i])); //开始写入新的 ZIP 文件条目并将流定位到条目数据的开始处
int b;
while((b = In.read()) != -1) {
out.write(b);
}
In.close();
}
out.flush();
out.close();
tempfile = new File(filepath);
String downFileNm=tempfile.getName();//取得要打包后要下载的文件的文件名(文件打包后的压缩文件)
System.out.println("downFileNm===="+downFileNm);
System.out.println("downFileNm.length===="+tempfile.length());
//以流的形式下载文件
InputStream fis = new BufferedInputStream(new FileInputStream(filepath));//获得目标下载文件输入流
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();//清空response
// 设置response的Header
//response.addHeader("Content-Disposition", "attachment;filename=" + new String(downFileNm.getBytes()));
//是下载的那个文件另存为的时候的名字
response.addHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(downFileNm,"UTF-8"));
response.addHeader("Content-Length", "" + tempfile.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");//设置响应的MIME类型
toClient.write(buffer);//你写的是buffer,也就是fis,也就是filepath这里对应的zip文件。这个是uuid.zip的话,上面的filename可以是abcd.zip
toClient.flush();
toClient.close();
}catch(IOException e) {
System.out.println("抛出异常:"+e.getMessage()+" "+"压缩文件出错!!");
}finally{
if(tempfile.exists())
{
tempfile.delete();
if(tempfile.exists())
{
System.out.println("---------删除临时文件失败-----------");
}else
{
System.out.println("------删除打包产生的临时文件------");
}
}
}
}
else{
System.out.println("请求下载文件的路径或者文件名存在问题");
}
//return null ;
最后要说的有这么几点一个是参数中含有汉字的问题,tomcat中相关文件没有配置的话,解决后台乱码有这么两种方式,get方式发送时,前台jsp页面两次encodeURI(encodeURI("url跳转地址串"))后台再解码即可。再有就是post方式发送,将要传递的参数放置在input hidden的标签中,后台直接request.getParameter("参数名")也可,强烈推荐后面的处理方式。
再有就是打包压缩的文件名这一块,为了预防同时刻,不同用户操作该文件下载,最好使用UUID号的方式命名。
再一次感谢许老师。