2010-01-24 29 views
13

Tôi đang làm việc với thư viện thao tác JPEG/EXIF ​​của bên thứ 3 (Mediautil) đang khiến tôi bị nhức đầu. Tôi muốn thay đổi dữ liệu EXIF ​​của một hình ảnh. Để thực hiện việc này, tôi cần phải viết phiên bản cập nhật vào một tệp tạm thời, xóa tệp gốc và sau đó đổi tên tệp tạm thời thành tên gốc.Cách chẩn đoán File.delete() trả về false/tìm các luồng không được xác định?

Vấn đề của tôi là cuộc gọi File.delete() không thành công và trả về false, có lẽ vì thư viện vẫn mở theo cách nào đó - nhưng tôi đã làm mọi thứ tôi có thể tìm thấy trong API để đóng tất cả các luồng. Thậm chí tệ hơn: vấn đề dường như phụ thuộc vào thời gian, và các bài kiểm tra Đơn vị xảy ra đôi khi thất bại và đôi khi không - nhưng mã là không phải là đa luồng.

Thật kỳ lạ, có một cuộc gọi thư viện loại bỏ sự cố - nhưng nó cũng xóa hình thu nhỏ EXIF ​​mà tôi thực sự không muốn. Và nhìn vào mã, tôi hoàn toàn không thể nhìn thấy nơi nó đóng cửa bất kỳ dòng mà nếu không có thể ở lại mở.

Bất kỳ ý tưởng nào về cách tấn công vấn đề này?

Edit: Đây là trên Windows XP, Java 6. Và một điều: Tôi đã phát hiện ra rằng nếu tôi gọi System.gc() trước khi gọi File.delete(), nó hoạt động - có lẽ vì điều đó gây nên một số finalizer. Vì vậy, nó chắc chắn có vẻ là một luồng không rõ ràng.

+2

Bạn có một trình antivirus chạy trên máy tính này? Đôi khi AV có thể tạm thời mở tệp sau khi bạn đã viết nó, dẫn đến hành vi rời rạc mà bạn đề cập đến đôi khi bạn "không may mắn" tại thời điểm bạn cố xóa. –

+0

Trên nền tảng nào? –

+0

@Neil ý tưởng hay, nhưng tắt AV không tạo ra sự khác biệt. –

Trả lời

5

Tôi sẽ tìm một số trợ giúp với trình gỡ lỗi tại đây. Đào nhanh qua các công cụ java.io cho thấy ứng cử viên finalize() có khả năng duy nhất là ở FileOutputStream. Vì vậy, tát một điểm ngắt trong đó, chạy chương trình của bạn, và thử và nhận được System.gc() để kích hoạt FileOutputStream.finalize() để phát hành luồng của bạn. Điều đó sẽ cung cấp cho bạn một câu trả lời là liệu đó có phải là vấn đề của bạn hay không.

Sau khi bạn có thể sao chép điều đó, thì bạn cần phải bắt đầu khớp với sự kiện của FileOutputStream trường hợp với quá trình hoàn tất. Trình gỡ rối tốt sẽ cung cấp cho bạn các mã định danh đối tượng JVM nội bộ cho mỗi đối tượng, vì vậy nếu bạn có thể theo dõi các OID khi chúng được tạo và theo dõi chúng khi chúng được hoàn thành, thì hy vọng bạn có thể kết hợp cuộc gọi chính với finalize với cuộc gọi cụ thể mới new FileOutputStream.

Có thể là một slog dài, tuy nhiên, tùy thuộc vào mức độ phức tạp của ứng dụng của bạn.

+0

Thử ngay bây giờ. Điều khó chịu là tôi khá chắc chắn thủ phạm là một FileInputStream, và những người được JVM sử dụng để tải các lớp học ... –

+0

OK, bây giờ tôi đã tìm thấy sự rò rỉ trong thư viện bằng cách sử dụng phương pháp này. –

0

Tại sao bạn không đổi tên tệp trước khi bạn mở thư viện? Sau đó, có thể sử dụng File.deleteOnExit của Java() để xóa tệp đã đổi tên. Ví dụ:

File jpeg = new File("image.jpg"); 
File temp = new File(jpeg + ".temp.jpg"); 
jpg.renameTo(temp); 
SomeObj result = exifLibrary(temp); // or exifLibrary(new FileInputStream(temp); 
OutputStream jpegStream = new FileOutputStream(jpeg); 
output.write(result.bytes(); 
output.close(); 
temp.deleteOnExit(); 
temp.delete(); 
+0

Vấn đề là không phải tất cả các tệp được đọc cần phải cập nhật dữ liệu EXIF. –

0

Và nhìn vào mã, tôi hoàn toàn không thể nhìn thấy nơi nó đóng bất kỳ con suối mà nếu không có thể ở lại mở.

Tôi nghĩ bạn có thể đã xác định được vấn đề thực sự; tức là API bạn đang sử dụng rò rỉ sẽ mở các tệp xử lý theo thiết kế.

Vì đây là thư viện nguồn mở bạn đang sử dụng, bạn có quyền truy cập mã nguồn. Vì vậy, bạn sẽ có thể xác nhận điều này, và nếu cần thiết sửa chữa nó cho chính mình. Và để trở thành một công dân tốt, hãy đóng góp sửa chữa của bạn trở lại dự án bằng cách gửi một bản vá.

+0

Tôi đã nói về việc xem xét mã nguồn của thư viện, không phải API. Và điều khó hiểu là một cuộc gọi thư viện dường như để sửa chữa vấn đề (như là một tác dụng phụ), nhưng tôi không thể nhìn thấy nơi nó đóng cửa bất kỳ dòng. Sau đó, một lần nữa, nó chỉ có thể được trigering một GC chạy finalizers ... –

+0

"Sau đó, một lần nữa, nó chỉ có thể được kích hoạt một GC chạy finalizers ..." - đó là lý thuyết của tôi quá. Nhưng có một cái gì đó để cố gắng để xem nếu đó là trường hợp: chạy ứng dụng của bạn với một đống HUGE để GC không có khả năng được kích hoạt bởi sự cạn kiệt đống. –

+0

Vì bạn đã tìm thấy sự rò rỉ, bình luận cuối cùng của tôi là loại dư thừa. Nhưng nếu bạn không tìm thấy sự rò rỉ, nó có thể đã giúp ... vì vậy tôi sẽ để nó cho hậu thế. –

1

Nếu bạn đang sử dụng một FileOutputStream, đóng nó rõ ràng cho phép xóa tập tin.

ví dụ: thay vì:

File myFile = new File("test.txt"); 
myCustomStreamProcess(new FileOutputStream(myFile)); 
boolean test = myFile.delete(); //May return false 

bạn nên làm:

File myFile = new File("test.txt"); 
FileOutputStream fos = new FileOutputStream(myFile); 
myCustomStreamProcess(fos); 
fos.close(); //Allow the document to be deleted afterwards 
boolean test = myFile.delete(); //Should always return true 
+1

Chào mừng bạn đến với Stack Overflow. Trước khi trả lời, bạn nên đọc kỹ câu hỏi và câu trả lời hiện có và đảm bảo bạn hiểu chúng. Câu trả lời của bạn không hữu ích vì nó giả định rằng tôi đang viết mã sử dụng FileOutputStream; như đã nêu trong câu hỏi, đó không phải là trường hợp. –

+0

Cảm ơn bạn đã nhận xét, câu hỏi của bạn đã giúp tôi tìm ra lý do khiến một số tệp thử nghiệm tạm thời không bị xóa nhưng tiếc là chưa thể bỏ phiếu. Tôi đã trả lời với hy vọng nó sẽ hữu ích cho những người khác gặp phải vấn đề này, tôi có thể xóa nó nếu không thích hợp. – nkatsar

Các vấn đề liên quan