使用Grails和Groovy,实现了这样的需求:
- zip文件上传
- zip文件上传后,在zip文件中加入自定义文本文件
在Grails实现复杂的数据录入上实现的本示例。示例如图:
准备工作:文件上传
首先说一下文件上传,在Grails中,借助Spring MVC的底层支持,实现还是很容易的。
视图:
<body>
<div style="margin-left: 15px;">
<g:message code="${flash.message}" />
<g:form action="uploadFile" method="post" enctype="multipart/form-data">
<input type="file" name="myFile" />
<input type="submit" value="上传" />
<input type="hidden" name="id" value="1"/>
</g:form>
</div>
</body>
在这里hidden了一个参数,是用于服务器端使用的。
在服务器端获取文件对象和id:
def uploadFile={
def id=params.id
def file=request.getFile("myFile")
下面的问题是:
- 如何异步的处理zip文件,同步处理是不可能的,那样浏览器端可能长期得不到响应
- 怎样向zip文件中加入文本文件
- 怎样动态生成文本文件,本例中是xml文件
异步并发的实现
先说异步问题,我希望尽量回避线程,简化开发,这样可以让具体实现人员比较容易上手。这里使用了java的并发api,可参见这里[cref 3801]。
写法类似这样:
class BookController {
ExecutorService executorService=Executors.newFixedThreadPool(2)
创建了个实例变量,允许2个并发。
可这样调用:
executorService.submit(new Runnable(){
public void run() {
如何向zip文件中加入文件
时间原因,没时间看apache commons compress等api是否能实现这个功能了。直接招呼java标准zip的api了。
基本思路是创建新的zip文件,将原zip文件中的各个entry加入到新的zip文件输出流中,最后,将自己的文本文件加入。
这里补充一句,从Grails中得到的file对象,生命周期应该是request作用域的。即,当上传文件请求到达服务器端后,在临时目录创建文件保存请求中的文件流。这个文件不可能长期保存,因为那样系统可能会因为文件增长瘫痪的。一般是通过一个Filter在生成响应发送给浏览器后删除本地的临时文件。
这样的话,异步线程的生命周期肯定会超过这个生命周期,因此要在request作用域内把该临时文件先复制到另外的目录下:
def path=request.session.servletContext.getRealPath("/WEB-INF")
def sourceFile=new File("""${path}/${id}.zip""")
def distFile=new File("""${path}/${id}-dist.zip""")file.transferTo(sourceFile)
下面是完整的生成新zip文件,并且加入文本文件的代码:
executorService.submit(new Runnable(){
public void run() {
ZipFile zipFile=new ZipFile(sourceFile)
ZipOutputStream outputStream=new ZipOutputStream(new FileOutputStream(distFile))
Enumeration entries=zipFile.entries()
while(entries.hasMoreElements()){
ZipEntry entry=entries.nextElement()
entry=new ZipEntry(entry.name)
println entry
outputStream.putNextEntry(entry) if(!entry.isDirectory()){
InputStream inputStream=zipFile.getInputStream(entry)
byte[] buffer=new byte[1024*1024]
for(int i=inputStream.read(buffer);i!=-1;i=inputStream.read(buffer)){
outputStream.write(buffer,0,i)
}
inputStream.close()
} outputStream.closeEntry()
}
zipFile.close() ZipEntry attachEntry=new ZipEntry("book.plist")
outputStream.putNextEntry(attachEntry) InputStream inputStream=new FileInputStream(attachedFile)
outputStream.write(getResults(id).getBytes())
inputStream.close()
outputStream.closeEntry()
outputStream.close()
println ">>>> zip create ok."
}
})
其中黑体中调用的是解决动态生成文本的内容,这里假定write方面里面的参数是String即可,是String.getBytes()。
动态生成自定义xml文本
这里可参阅groovy生成xml,这里使用到了MarkupBuilder。
代码如下:
def getResults(def bookId){
def writer=new StringWriter()
def xmlResults=new MarkupBuilder(writer) xmlResults.plist(version:1.0){
dict{
key ‘key’
string ’12′
}
} def content="""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
${writer}
""" return content
}
生成的文本类似这样:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version=’1.0′>
<dict>
<key>key</key>
<string>12</string>
</dict>
</plist>
完整源代码见:
http://easymorse.googlecode.com/svn/tags/BookProto-0.5/