3

Web アプリの画像アップロード機能に取り組んでいますが、apache commons fileupload の「FileCleaningTracker」で奇妙な問題が発生しています。インスタンス変数 FileCleaningTracker を持つ ImageUploadService があり、次に DiskFileItemFactory のインスタンスを作成し、FileCleaningTracker を参照するアップロード メソッドがあります。アップロード メソッドが正常に完了した後、DiskFileItemFactory の FileCleaningTracker を null に設定するので、DiskFileItemFactory が期待されます。がガベージ コレクションされると、FileCleaningTracker の PhantomReference の基になるサブクラスに通知されるため、DiskFileItemFactory が作成した一時ファイルを削除します。

しかし、それは、アップロード メソッドの最後で DiskFileItemFactory を null にして System.gc() を呼び出すまでは起こりません (DiskFileItemFactory を null にするだけでは役に立ちません)。これは私には非常に奇妙に思えます。これが私のコードです:

    @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 インスタンスがガベージ コレクションされていないためです (そうすべきではない理由がわかりませんでした)。または、GC されているが、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-fileuploadバージョン 1.2.2 とcommons-ioバージョン 1.3.2 を使用しています。

于 2012-11-19T22:50:30.420 に答える