2012-01-05 4 views
39

Sono in procinto di scrivere un servizio WCF che consentirà a un sito Web ASP.Net di recuperare i file (in base a this article). Il mio problema è che quando restituisco il flusso, è vuoto.Restituzione di un flusso da File.OpenRead()

Per semplicità, ho isolato il codice in un semplice WinForms app per cercare di trovare qual è il problema con la restituzione di un torrente, e questo è il codice:

private Stream TestStream() 
    { 
     Stream fs = File.OpenRead(@"c:\testdocument.docx"); 
     return fs; 
    } 

    // This method converts the filestream into a byte array so that when it is 
    // used in my ASP.Net project the file can be sent using response.Write 
    private void Test() 
    {    
     System.IO.MemoryStream data = new System.IO.MemoryStream(); 
     System.IO.Stream str = TestStream(); 

     str.CopyTo(data); 
     byte[] buf = new byte[data.Length]; 
     data.Read(buf, 0, buf.Length);      
    } 

Il risultato di questo codice è che buf è lungo 12.587 byte (la lunghezza corretta del file) ma contiene solo 0.

Il documento di Word si apre senza problemi se lo provo, mi manca qualcosa di ovvio?

+1

Sei in esecuzione come amministratore? Prova a estrarre il documento da "Documenti" o da un'altra cartella diversa da root. – keyboardP

+1

@keyboard - un buon consiglio ma produrrebbe un'eccezione, non '0's e lunghezza corretta. –

+0

@HenkHolterman - Ah si, è vero. – keyboardP

risposta

31

si è dimenticato di chiedere:

str.CopyTo(data); 
data.Seek(0, SeekOrigin.Begin); // <-- missing line 
byte[] buf = new byte[data.Length]; 
data.Read(buf, 0, buf.Length); 
+0

Grazie Ken, ha funzionato a meraviglia. – GrandMasterFlush

+0

cosa succede se la ricerca non è consentita? –

1

Provare a cambiare il codice per questo:

private void Test() 
{    
    System.IO.MemoryStream data = new System.IO.MemoryStream(TestStream()); 

    byte[] buf = new byte[data.Length]; 
    data.Read(buf, 0, buf.Length);      
} 
3

È necessario

str.CopyTo(data); 
    data.Position = 0; // reset to beginning 
    byte[] buf = new byte[data.Length]; 
    data.Read(buf, 0, buf.Length); 

E dal momento che il metodo Test() sta imitando il cliente dovrebbe a Close() o Dispose() il str S tream. E anche il memory-stream, appena fuori dal principio.

+0

Grazie Henk, ha funzionato bene. Il codice WCF chiuderà/smaltirà i flussi di conseguenza, l'ho lasciato sull'app di test. – GrandMasterFlush

+0

Ho raccolto, ma ciò rende un'app test incompleta. –

+0

Concordato, ma l'app era solo per test/debug del problema, non per un'imbracatura di test o per qualsiasi cosa implicata in un test formale. È stato più facile per me trasferire il codice in una semplice app, quindi provare a eseguire il debug del servizio WCF sul proprio server. – GrandMasterFlush

12

Opzioni:

  • Usa data.Seek come suggerito da ken2k
  • Utilizzare il po 'più semplice Position proprietà:

    data.Position = 0; 
    
  • Utilizzare il ToArray chiamata in MemoryStream per rendere la vita più semplice per iniziare con:

    byte[] buf = data.ToArray(); 
    

La terza opzione sarebbe il mio approccio preferito.

nota che si dovrebbe avere una dichiarazione using per chiudere automaticamente il flusso di file (e facoltativamente per il MemoryStream), e mi piacerebbe aggiungere una direttiva using per System.IO per rendere il vostro pulitore di codice:

byte[] buf; 
using (MemoryStream data = new MemoryStream()) 
{ 
    using (Stream file = TestStream()) 
    { 
     file.CopyTo(data); 
     buf = data.ToArray(); 
    } 
} 

// Use buf 

È potrebbe anche voler creare un metodo di estensione su Stream per farlo in un unico posto, ad es.

public static byte[] CopyToArray(this Stream input) 
{ 
    using (MemoryStream memoryStream = new MemoryStream()) 
    { 
     input.CopyTo(memoryStream); 
     return memoryStream.ToArray(); 
    } 
} 

Nota non che questo non chiudere il flusso di input.

+0

Grazie Jon, è un modo molto più bello di farlo. Il servizio WCF utilizza le istruzioni using, le ho semplicemente omesse dal codice di test per renderlo più facile da leggere quando l'ho postato qui. – GrandMasterFlush

5

Hai dimenticato di ripristinare la posizione del flusso di memoria:

private void Test() 
{    
    System.IO.MemoryStream data = new System.IO.MemoryStream(); 
    System.IO.Stream str = TestStream(); 

    str.CopyTo(data); 
    // Reset memory stream 
    data.Seek(0, SeekOrigin.Begin); 
    byte[] buf = new byte[data.Length]; 
    data.Read(buf, 0, buf.Length);      
} 

Aggiornamento:

C'è un'altra cosa da notare: Si paga di solito non ignorare i valori restituiti di metodi. Un'implementazione più robusta dovrebbe verificare quanti byte sono stati letti dopo la restituzione della chiamata:

private void Test() 
{    
    using(MemoryStream data = new MemoryStream()) 
    { 
     using(Stream str = TestStream()) 
     { 
      str.CopyTo(data); 
     } 
     // Reset memory stream 
     data.Seek(0, SeekOrigin.Begin); 
     byte[] buf = new byte[data.Length]; 
     int bytesRead = data.Read(buf, 0, buf.Length); 

     Debug.Assert(bytesRead == data.Length, 
        String.Format("Expected to read {0} bytes, but read {1}.", 
         data.Length, bytesRead)); 
    }      
}