2009-05-05 10 views
7

Après avoir écrit du contenu traité dans un flux de sortie, j'ai besoin de revoir le début du flux et d'écrire des métadonnées de contenu. Les données que j'écris sont très volumineuses, jusqu'à 4 Go, et peuvent être écrites soit directement dans un fichier, soit dans un tampon en mémoire, en fonction de divers facteurs environnementaux.Comment puis-je implémenter un OutputStream que je peux rembobiner?

Comment puis-je implémenter un OutputStream qui me permet d'écrire des en-têtes après avoir terminé l'écriture du contenu?

+0

Ce n'est pas le cas, malheureusement, mais c'est en grande partie parce que j'ai dû changer de conception pour d'autres raisons. C'est toujours une bonne réponse. –

Répondre

10

Voici un flux de sortie de fichier à accès aléatoire. Notez que si vous l'utilisez pour une grande quantité de sortie en streaming, vous pouvez l'insérer temporairement dans un BufferedOutputStream pour éviter de nombreuses petites écritures (assurez-vous de le vider avant de jeter le wrapper ou d'utiliser directement le flux sous-jacent).

import java.io.*; 

/** 
* A positionable file output stream. 
* <p> 
* Threading Design : [x] Single Threaded [ ] Threadsafe [ ] Immutable [ ] Isolated 
*/ 

public class RandomFileOutputStream 
extends OutputStream 
{ 

// ***************************************************************************** 
// INSTANCE PROPERTIES 
// ***************************************************************************** 

protected RandomAccessFile    randomFile;        // the random file to write to 
protected boolean      sync;         // whether to synchronize every write 

// ***************************************************************************** 
// INSTANCE CONSTRUCTION/INITIALIZATON/FINALIZATION, OPEN/CLOSE 
// ***************************************************************************** 

public RandomFileOutputStream(String fnm) throws IOException { 
    this(fnm,false); 
    } 

public RandomFileOutputStream(String fnm, boolean syn) throws IOException { 
    this(new File(fnm),syn); 
    } 

public RandomFileOutputStream(File fil) throws IOException { 
    this(fil,false); 
    } 

public RandomFileOutputStream(File fil, boolean syn) throws IOException { 
    super(); 

    File        par;         // parent file 

    fil=fil.getAbsoluteFile(); 
    if((par=fil.getParentFile())!=null) { IoUtil.createDir(par); } 
    randomFile=new RandomAccessFile(fil,"rw"); 
    sync=syn; 
    } 

// ***************************************************************************** 
// INSTANCE METHODS - OUTPUT STREAM IMPLEMENTATION 
// ***************************************************************************** 

public void write(int val) throws IOException { 
    randomFile.write(val); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void write(byte[] val) throws IOException { 
    randomFile.write(val); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void write(byte[] val, int off, int len) throws IOException { 
    randomFile.write(val,off,len); 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void flush() throws IOException { 
    if(sync) { randomFile.getFD().sync(); } 
    } 

public void close() throws IOException { 
    randomFile.close(); 
    } 

// ***************************************************************************** 
// INSTANCE METHODS - RANDOM ACCESS EXTENSIONS 
// ***************************************************************************** 

public long getFilePointer() throws IOException { 
    return randomFile.getFilePointer(); 
    } 

public void setFilePointer(long pos) throws IOException { 
    randomFile.seek(pos); 
    } 

public long getFileSize() throws IOException { 
    return randomFile.length(); 
    } 

public void setFileSize(long len) throws IOException { 
    randomFile.setLength(len); 
    } 

public FileDescriptor getFD() throws IOException { 
    return randomFile.getFD(); 
    } 

} // END PUBLIC CLASS 
2

Si vous connaissez la taille de l'en-tête, vous pouvez d'abord écrire un en-tête vide, puis revenir en arrière pour le corriger avec RandomAccessFile à la fin. Si vous ne connaissez pas la taille de l'en-tête, il est fondamental que les systèmes de fichiers ne vous permettent généralement pas d'insérer des données. Vous devez donc écrire dans un fichier temporaire, puis écrire le fichier réel.

+0

Nous connaissons la taille de l'en-tête, mais nous ne pouvons pas trouver un OuptutStream générique qui permette d'écrire à un point arbitraire du flux. –

+0

Vous l'écrivez. Ferme le fichier Ouvrez un fichier RandomAccessFile. Écris l'en-tête. Fermez le RandomAccessFile. –

+1

(Notez que les performances de RandomAccessFile par opération sont nulles, utilisez donc des opérations de gros blocs.) –

0

Une façon serait d'écrire le contenu initial à un premier tampon de mémoire, puis les en-têtes dans le flux de sortie « réel », suivi par rinçage de tampon contenu, et à partir de là, écrivez simplement dans le flux non tamponné. Il semble que le segment initial ne soit pas trop long, pour rendre le tampon raisonnable. En ce qui concerne l'implémentation, vous pouvez utiliser ByteArrayOutputStream pour la mise en mémoire tampon, puis demander à votre classe OutputStream de prendre un flux de sortie "réel" en tant qu'argument; et juste basculer entre les deux si nécessaire. Vous devrez peut-être étendre l'API OutputStream pour permettre de définir ce que les métadonnées doivent écrire, car ces déclencheurs passent du mode mis en mémoire tampon.

Comme mentionné par l'autre réponse, RandomAccessFile fonctionnerait aussi, bien que n'implémenterait pas OutputStream.

+1

"Les données que j'écris sont très volumineuses, jusqu'à 4 Go" – DJClayworth