今天要说的是关于文件上传这个普通的不能再普通的话题,为什么会有这东西。 因为“已故项目”的一个普遍存在的需求:大文件上传。 由于“已故项目”是作为一个IaaS和PaaS的综合平台,可能会涉及到如各种应用程序,以及系统镜像这种超大文件的上传。 所以需要解决一下在浏览器端大文件上传的问题,于是有了今天的这个主题,虽然项目不在了。但是作为学习和巩固还是很有必要记录一下的。
一些需求
其实以前也做过文件上传的东西,但是只是简单的实现了一下。 但是其实向来还是有很多细节的东西需要考虑的:
- 上传文件前的check动作,文件是否存在,文件大小是否超过限制,是否需要覆盖已经存在的文件等等;
- 文件上传过程中如何实现暂停与恢复,这就涉及到文件分段写入;
- 对于超大文件直接分块上传;
走了捷径
文件上传其实有很多细节值得推敲,但是如果只靠我等屁民可能还是稍微花一点时间来做这些东西,因为就单个文件上传的动作而言就有好几个与服务器端交互的过程。在伟大的Google帮助下,找到了一款Flex+PHP的文件上传组件, 读了它的博客以后觉得这东西很多细节做的很不错,本上包含了上面说的所有需求,在继续寻找无果的情况下,屁民毅然决然决定保留Flex部分,重新用Java实现之前Php脚本提供的功能,姑且把它当作是个稍微复杂一点的表单吧,毕竟无论是Html或者Flex和服务器交互都是走的Http协议,所以本质上没什么不同。
前端代码动不得:
<div id="uploader">
<script type="text/javascript">
/** CONFIG SETTINGS FOR THE FILE UPLOADER **/
var flashvars = {
/**
* the url of the upload target file which processes the file uploads
*/
uploadUrl : 'upload.php',
/**
* the maximum file size that can be upload in Bytes
*/
maxFileSize : (1024 * 1024 * 1024),
/**
* the maximum size of a single post request in Bytes that can be uploaded
* As the BigFileUpload transfers big files with several requests,
* this setting specifies the maximum size of one of these requests.
*/
maxPostSize : (1024 * 1024 * 1024)
/**
* a comma seperated list of file endings that are allowed to be uploaded
*/
//fileFilter : 'jpg,jpeg,png,gif'
/**
* The maximum number of files that can be added to the upload list
*/
//maxFiles : 20
};
swfobject.embedSWF("FileUploaderStandalone.swf", "uploader", "500", "350", "10.0.0", "", flashvars);
</script>
</div>
主要设计几个参数:
- uploadUrl: 文件上传的访问路径
- maxFileSize: 单个文件的最大大小
- maxPostSize: 和maxFileSize保持一致即可
Flex的东西就不多说了,因为完全不懂。 主要说一下在看了Php脚本以及一些实验后得出的一些简单结论。
首先这个Flex虽然可以选择多文件,但是实际过程中也是顺序上传的,这对服务器端是个好消息,不用考虑多文件同时上传的问题。 对于每个单个文件的上传主要涉及到两个过程:1,文件检查;2,上传。 说直白一点就是上传文件前,先将文件的基本信息通过get请求到服务器,服务器段代码再做一些判断,比如文件是否存在等等,如果服务器端返回success则表示可以上传,近而将文件post到服务器段。
下面展示一个文件上传的HTTP过程:
- 正常的一个文件上传过程
- 文件已经存在的上传过程
在根据这个基本过程,利用common-fileupload实现响应的过程就很简单了。 当然现在只是处理了基本的文件上传过程,对于文件上传过程中的暂停与恢复过程暂时并未涉及。相对与简单的文件上传,暂停与恢复涉及的东西更多。 今天主要是写的东西主要是设计一个分析过程。 屁民不懂php也不懂flex还是硬着头皮做了一点尝试。下面是上传Servlet的主要实现:
protected void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter out = resp.getWriter();
UploadAction paser = loadUploadRequestPaser(req);
try {
paser.doAction(req);
} catch (FileUploadException e) {
out.print(MessageHandler.getErrorMessage(e));
return;
} catch(Exception e ){
out.print(MessageHandler.getErrorMessage(e.getMessage()));
return;
}
out.print(MessageHandler.getSuccessMessage());
}
private UploadAction loadUploadRequestPaser(HttpServletRequest req) {
String fileAction = req.getParameter("fileAction");
if (fileAction.equals("check")) {
return new FileCheckAction();
} else if(fileAction.equals("upload")) {
return new OrdninaryUploadAction();
}
else {
return new OrdninaryUploadAction();
}
}
更多的代码请异步https://github.com/yunlzheng/flex-fileupload-with-java。
一些其他的话
在实现这个小功能的过程中,我都尽量的通过小步快跑的方式来做,实现过程中很多问题的修改方法都参考了《重构》中的思想。 其实怎么说这些东西无非是让我们写出更加具有可扩展,可读,健壮,优雅的代码。其中很多东西都是平时有用到的,只是没有如书中如此系统的总结一番。 如果你是一个刚从事软件开发不久的同学,希望有机会一定要看看这本书;如果你是一个已经从事多年软件开发的人,那么这些小技法肯定早就熟悉的不得了,但是再看一次别有一番风味。
话说写到这里,已经淡忘了刚开始写这篇文章时激动的心情。写代码或者写博客总能让人渐渐平静下来。所以我更想保护我自己的这片净土了。