48

Bạn không thể nối thêm ObjectOutputStream?Phụ thêm vào ObjectOutputStream

Tôi đang cố gắng thêm vào danh sách đối tượng. Đoạn mã sau là hàm được gọi bất cứ khi nào công việc được hoàn thành.

FileOutputStream fos = new FileOutputStream 
      (preferences.getAppDataLocation() + "history" , true); 
ObjectOutputStream out = new ObjectOutputStream(fos); 

out.writeObject(new Stuff(stuff)); 
out.close(); 

Nhưng khi tôi cố đọc nó, tôi chỉ nhận được tệp đầu tiên trong tệp. Sau đó, tôi nhận được java.io.StreamCorruptedException.

Để đọc Tôi đang sử dụng

FileInputStream fis = new FileInputStream 
     (preferences.getAppDataLocation() + "history"); 
ObjectInputStream in = new ObjectInputStream(fis);  

try{ 
    while(true) 
     history.add((Stuff) in.readObject()); 
}catch(Exception e) { 
    System.out.println(e.toString()); 
} 

Tôi không biết có bao nhiêu đối tượng sẽ có mặt vì vậy tôi đang đọc khi không có trường hợp ngoại lệ. Từ những gì Google nói điều này là không thể. Tôi đã tự hỏi nếu có ai biết một cách?

+0

Bạn có nhận được bất kỳ thứ gì từ luồng hay không ngoại trừ lần đầu tiên trong vòng lặp? – skaffman

+0

nó đọc đối tượng đầu tiên tôi lưu sau đó tôi nhận được ngoại lệ. –

+0

Trong đoạn mã trên, tôi chỉ thấy một đối tượng được ghi vào tệp, vì vậy chỉ có một đối tượng được đọc, đúng không? – aperkins

Trả lời

67

Đây là lừa: lớp con ObjectOutputStream và ghi đè lên writeStreamHeader phương pháp:

public class AppendingObjectOutputStream extends ObjectOutputStream { 

    public AppendingObjectOutputStream(OutputStream out) throws IOException { 
    super(out); 
    } 

    @Override 
    protected void writeStreamHeader() throws IOException { 
    // do not write a header, but reset: 
    // this line added after another question 
    // showed a problem with the original 
    reset(); 
    } 

} 

Để sử dụng nó, chỉ cần kiểm tra xem các tập tin lịch sử tồn tại hay không và nhanh chóng trong hai dòng appendable này (trong trường hợp tập tin tồn tại = chúng tôi append = chúng ta không muốn một header) hoặc stream gốc (trong trường hợp file không tồn tại = chúng ta cần header).

Sửa

tôi đã không hài lòng với việc đặt tên đầu tiên của lớp. Mới hơn một: nó mô tả của những gì nó cho 'thay vì sau đó là 'thế nào nó được thực hiện'

Sửa

Thay đổi tên một lần nữa, để làm rõ, mà dòng này chỉ dành cho phụ thêm vào một hiện tập tin.Không thể sử dụng tệp này để tạo tệp mới với dữ liệu đối tượng.

Sửa

Added một cuộc gọi đến reset() sau this question cho thấy phiên bản gốc mà chỉ gạt writeStreamHeader là một không-op có thể theo một số điều kiện tạo ra một dòng suối mà không thể được đọc.

+0

Thông minh! Tôi nghĩ * tiêu đề luồng là vấn đề duy nhất, trong trường hợp này, điều này sẽ hoạt động tốt, nhưng bạn đã thử nghiệm nó chưa? –

+0

Cảm ơn bạn đã bình luận :-) Có, tôi đã đăng mã thử nghiệm (chỉ cần quên đề cập đến nó trong câu trả lời) –

+0

+1, cảm ơn. * Cảnh báo: * writeStreamHeader() được gọi bởi constructor * ObjectOutputStream *. Điều này làm cho nó ít nhất là khó khăn cho một lớp duy nhất để xử lý cả hai trường hợp. Bạn có thể tạo một phương thức factory đơn giản trả về fileExists? mới AppendingObjectOutputStream (out): new ObjectOutputStream (ngoài); –

7

Do định dạng chính xác của tệp được tuần tự hóa, việc bổ sung sẽ thực sự làm hỏng nó. Bạn phải viết tất cả các đối tượng vào tệp dưới dạng một phần của cùng một luồng hoặc nếu không, tệp sẽ bị lỗi khi đọc siêu dữ liệu luồng khi nó đang chờ một đối tượng.

Bạn có thể đọc số Serialization Specification để biết thêm chi tiết hoặc (dễ dàng hơn) đọc this thread nơi Roedy Green nói về cơ bản những gì tôi vừa nói.

13

Khi số API cho biết, hàm tạo ObjectOutputStream ghi tiêu đề luồng tuần tự hóa vào luồng cơ bản. Và tiêu đề này được dự kiến ​​sẽ chỉ một lần, ở đầu tệp. Vì vậy, hãy gọi số

new ObjectOutputStream(fos); 

nhiều lần trên FileOutputStream liên quan đến cùng một tệp sẽ ghi tiêu đề nhiều lần và làm hỏng tệp.

5

Cách dễ nhất để tránh vấn đề này là giữ cho OutputStream mở khi bạn ghi dữ liệu, thay vì đóng nó sau mỗi đối tượng. Gọi số reset() có thể được khuyến khích để tránh rò rỉ bộ nhớ.

Cách khác là đọc tệp dưới dạng một chuỗi các ObjectInputStream liên tiếp. Nhưng điều này yêu cầu bạn phải đếm số byte bạn đọc (điều này có thể được implementd với một FilterInputStream), sau đó đóng InputStream, mở lại nó, bỏ qua nhiều byte và chỉ sau đó bọc nó trong một ObjectInputStream().

0

Làm thế nào về trước mỗi lần bạn chắp thêm một đối tượng, đọc và sao chép tất cả dữ liệu hiện tại trong tệp và sau đó ghi đè tất cả lại với nhau để gửi.

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