除非显式调用System.gc(),否则org.apache.commons.io.FileCleaningTracker不会删除临时文件?

时间:2012-09-06 14:05:33

标签: jvm

我正在为我的网络应用程序开发上传图片功能,并且我遇到了来自apache commons fileupload的“FileCleaningTracker”的奇怪问题。我有一个带有实例变量FileCleaningTracker的ImageUploadService,然后我有一个创建DiskFileItemFactory实例然后引用FileCleaningTracker的上传方法,在upload方法成功完成后,我将DiskFileItemFactory的FileCleaningTracker设置为null,所以我希望DiskFileItemFactory要进行垃圾回收,然后会通知FileCleaningTracker中PhantomReference的基础子类,因此删除DiskFileItemFactory创建的临时文件。

但是直到我将DiskFileItemFactory归零并且在upload方法结束时调用System.gc()(仅使DiskFileItemFactory无效)时才会发生这种情况。这对我来说似乎很奇怪。这是我的代码:

    @Override
    public void upload(final HttpServletRequest request) {

        ValidateUtils.checkNotNull(request, "upload request");

        final File tmp = new File(this.tempFolder);

        if (!tmp.exists()) {
            tmp.mkdir();
        }

        DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(this.sizeThreshold, tmp);

        fileItemFactory.setFileCleaningTracker(this.fileCleaningTracker);

        ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);

        List items;
        try {
            items = uploadHandler.parseRequest(request);
        } catch (final FileUploadException e) {
            throw new ImageUploadServiceException("Error parsing the http servlet request for image upload.", e);
        }

        final Iterator it = items.iterator();
        while (it.hasNext()) {

            final DiskFileItem item = (DiskFileItem) it.next();
            if (item.isFormField()) {

                // log message
            } else {

                final String fileName = item.getName();

                final File destination = this.createFileForUpload(fileName, this.uploadFolder);

                FileChannel outChannel;
                try {
                    outChannel = new FileOutputStream(destination).getChannel();
                } catch (final FileNotFoundException e) {
                    throw new ImageUploadServiceException(e);
                }
                FileChannel inChannel = null;
                try {

                    inChannel = new FileInputStream(item.getStoreLocation()).getChannel();
                    outChannel.transferFrom(inChannel, 0, item.getSize());
                } catch (final IOException e) {
                    throw new ImageUploadServiceException(String.format("Error uploading image to '%s/%s'.", this.uploadFolder, destination.getName()), e);
                } finally {
                    IOUtils.closeChannel(inChannel);
                    IOUtils.closeChannel(outChannel);
                }

            }
        }
             fileItemFactory.setFileCleaningTracker(null);
}

上面的代码导致每次上传都在temp文件夹中创建一个文件,但最后却没有通过“fileCleaningTracker”删除它,可能是因为DiskFileItemFactory实例没有被垃圾收集(我没看清为什么它应该'或者已经GCed但没有通过fileCleaningTracker中的PhantomReference通知(PhantomReference有多可靠?)

我等了10分钟,文件仍在那里,所以不应该因为GC没有运行。并且没有例外。

现在,如果我添加以下代码,则每次上传后都会删除临时文件:

    fileItemFactory = null;
    System.gc();

这对我来说非常奇怪,因为我希望fileItemFactory可以在没有对System.gc()的明确调用的情况下进行GC。

任何意见都将受到赞赏。

谢谢。

1 个答案:

答案 0 :(得分:6)

我有同样的问题。即使在服务器关闭后,也永远不会删除临时文件:GC进程尚未启动,因此FileCleaningTracker无法从ReferenceQueue获取要删除的跟踪文件,并且所有文件都保留在硬盘驱动器上。< / p>

由于我的应用程序的特定行为,我必须在每次上传后清理(文件可能非常大)。而不是使用标准org.apache.commons.io.FileCleaningTracker我很幸运能够用我自己的实现来覆盖这个类:

/**
 * Cleaning tracker to clean files after each upload with special method invocation.
 * Not thread safe and must be used with 1 factory = 1 thread policy.
 */
public class DeleteFilesOnEndUploadCleaningTracker extends FileCleaningTracker {

     private List<String> filesToDelete = new ArrayList();

     public void deleteTemporaryFiles() {
         for (String file : filesToDelete) {
             new File(file).delete();
         }
         filesToDelete.clear();
     }

     @Override
     public synchronized void exitWhenFinished() {
         deleteTemporaryFiles();
     }

     @Override
     public int getTrackCount() {
         return filesToDelete.size();
     }

     @Override
     public void track(File file, Object marker) {
         filesToDelete.add(file.getAbsolutePath());
     }

     @Override
     public void track(File file, Object marker, FileDeleteStrategy deleteStrategy) {
         filesToDelete.add(file.getAbsolutePath());
     }

     @Override
     public void track(String path, Object marker) {
         filesToDelete.add(path);
     }

     @Override
     public void track(String path, Object marker, FileDeleteStrategy deleteStrategy) {
         filesToDelete.add(path);
     }
 }

如果这是正确的情况,只需将上述类的实例注入DiskFileItemFactory

DeleteFilesOnEndUploadCleaningTracker tracker = new DeleteFilesOnEndUploadCleaningTracker();
fileItemFactory.setFileCleaningTracker(tracker);

在完成上传项目的工作后,不要忘记调用清理方法:

tracker.deleteTemporaryFiles();

忘记提及:我使用commons-fileupload版本1.2.2和commons-io版本1.3.2。