使用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/