3

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

但这不会发生,直到我在上传方法结束时将 DiskFileItemFactory 清空并调用 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);
}

上面的代码导致每次上传都会在临时文件夹中创建一个文件,但最后不会被“fileCleaningTracker”删除,可能是因为 DiskFileItemFactory 实例没有被垃圾收集(我看不出为什么它不应该有)或者它已被 GCed 但未由 fileCleaningTracker 中的 PhantomReference 通知(PhantomReference 的可靠性如何?)

我等了10分钟,文件还在,应该不是GC没有运行的原因。并且没有例外。

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

    fileItemFactory = null;
    System.gc();

这对我来说看起来很奇怪,因为我希望在没有显式调用 System.gc() 的情况下对 fileItemFactory 进行 GC。

任何输入将不胜感激。

谢谢你。

4

1 回答 1

6

我也有同样的问题。即使服务器关闭,临时文件也永远不会删除:GC 进程尚未启动,因此FileCleaningTracker没有机会从中删除跟踪文件ReferenceQueue,所有文件都保留在硬盘上。

由于我的应用程序的特定行为,我必须在每次上传后进行清理(文件可能非常大)。而不是使用标准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-fileupload1.2.2 和commons-io1.3.2 版本。

于 2012-11-19T22:50:30.420 回答