2009-10-31 3 views
12

C'è un modo per caricare un file, salvarlo su uno Stream, questo Stream lo salverò temporaneamente in una sessione e, alla fine, cercherò di visualizzare in anteprima questo file caricato che si trova in questa sessione ??Come posso caricare un file e salvarlo su uno Stream per un'ulteriore anteprima usando C#?

Ad esempio, un file pdf.

Grazie !!

CURA

Ecco quello che sto cercando di fare:

HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase; 
byte[] buffer = new byte[hpf.InputStream.Length]; 
MemoryStream ms = new MemoryStream(buffer); 
ms.Read(buffer, 0, (int)ms.Length); 
Session["pdf"] = ms.ToArray(); 
ms.Close(); 

E in un altro metodo, sto facendo questo:

byte[] imageByte = null; 

imageByte = (byte[])Session["pdf"]; 

Response.ContentType = "application/pdf"; 
Response.Buffer = true; 
Response.Clear(); 
Response.BinaryWrite(imageByte); 

Ma niente happends .. il mio browser apre anche una pagina nem per mostrare il file pdf, ma viene mostrata una finestra che dice che il file non è un pdf (o qualcosa come il file non inizia con pdf, I non ho capito che)

+0

riferimento al mio esempio qui sotto che scrive il byte array come FileResult. È più sicuro di Response.BinaryWrite poiché stai usando aspnet mvc. Inoltre, impiegare un lettore di stream migliore (vedere la mia modifica di seguito) e scorrere il codice per verificare che il file caricato venga effettivamente scaricato nella sessione. ;) –

+1

Il problema principale con il tuo codice è che non stai affatto leggendo dal flusso di input. Stai creando un buffer con le giuste dimensioni, ma leggi dal flusso di memoria vuoto nel buffer invece che dal flusso di input. Quello che ottieni è solo un array pieno di zeri, che ovviamente non funziona come un file PDF. – Guffa

risposta

15

Sure is. Carico file (PDF/immagini) sul mio db nel mio app. Il mio oggetto modello in realtà memorizza il file come una matrice di byte, ma per altre funzioni devo convertire da e verso i flussi in modo tale che sia altrettanto facile conservarlo nel formato del flusso.

Ecco alcuni esempi di codice (copia n incollare) dal mio app-

Il File oggetto che uso per spostare i file (PDF/immagini) in giro:

public class File : CustomValidation, IModelBusinessObject 
{ 
    public int ID { get; set; } 
    public string MimeType { get; set; } 
    public byte[] Data { get; set; } 
    public int Length { get; set; } 
    public string MD5Hash { get; set; } 
    public string UploadFileName { get; set; } 
} 

..il PdfDoc tipo specificamente per i file PDF:

public class PdfDoc : File 
{ 
    public int ID { get; set; } 
    public int FileID 
    { 
     get { return base.ID; } 
     set { base.ID = value; } 
    } 
    [StringLength(200, ErrorMessage = "The Link Text must not be longer than 200 characters")] 
    public string LinkText { get; set; } 


    public PdfDoc() { } 

    public PdfDoc(File file) 
    { 
     MimeType = file.MimeType; 
     Data = file.Data; 
     Length = file.Length; 
     MD5Hash = file.MD5Hash; 
     UploadFileName = file.UploadFileName; 
    } 

    public PdfDoc(File file, string linkText) 
    { 
     MimeType = file.MimeType; 
     Data = file.Data; 
     Length = file.Length; 
     MD5Hash = file.MD5Hash; 
     UploadFileName = file.UploadFileName; 

     LinkText = linkText; 
    } 
} 

.. un esempio di un'azione che riceve più parti POST per caricamento di file:

Ora è possibile vedere passare l'oggetto file al mio servizio qui, ma è possibile scegliere di aggiungerlo alla sessione e passare di nuovo un ID alla vista 'anteprima', ad esempio.

Infine, ecco un'azione generica che uso per rendere i file al client (si potrebbe avere qualcosa di simile rendere i file/flusso dalla sessione):

// 
    // GET: /FileManager/GetFile/ID 
    [OutputCache(Order = 2, Duration = 600, VaryByParam = "ID")] 
    public ActionResult GetFile(int ID) 
    { 
     FileService svc = ObjectFactory.GetInstance<FileService>(); 

     KsisOnline.Data.File result = svc.GetFileByID(ID); 

     return File(result.Data, result.MimeType, result.UploadFileName); 
    } 

EDIT:
I notato ho bisogno di più campioni di spiegare il già

per l'azione di caricamento sopra, la classe FileUploadResultDTO:

public class FileUploadResultDTO 
    { 
     public List<File> Files { get; set; } 
     public Int32 TotalBytes { get; set; } 
     public string ErrorMessage { get; set; } 
    } 

E la funzione GetFilesFromRequest:

public static FileUploadResultDTO GetFilesFromRequest(HttpContextWrapper contextWrapper) 
    { 
     FileUploadResultDTO result = new FileUploadResultDTO(); 
     result.Files = new List<File>(); 

     foreach (string file in contextWrapper.Request.Files) 
     { 
      HttpPostedFileBase hpf = contextWrapper.Request.Files[file] as HttpPostedFileBase; 
      if (hpf.ContentLength > 0) 
      { 
       File tempFile = new File() 
       { 
        UploadFileName = Regex.Match(hpf.FileName, @"(/|\\)?(?<fileName>[^(/|\\)]+)$").Groups["fileName"].ToString(), // to trim off whole path from browsers like IE 
        MimeType = hpf.ContentType, 
        Data = FileService.ReadFully(hpf.InputStream, 0), 
        Length = (int)hpf.InputStream.Length 
       }; 
       result.Files.Add(tempFile); 
       result.TotalBytes += tempFile.Length; 
      } 
     } 

     return result; 
    } 

E infine (spero di aver tutto il necessario ora) questa funzione ReadFully. Non è il mio design. L'ho preso dalla rete - la lettura del flusso può essere complicata.Trovo questa funzione è il modo più efficace per leggere tutto di un flusso:

/// <summary> 
    /// Reads data from a stream until the end is reached. The 
    /// data is returned as a byte array. An IOException is 
    /// thrown if any of the underlying IO calls fail. 
    /// </summary> 
    /// <param name="stream">The stream to read data from</param> 
    /// <param name="initialLength">The initial buffer length</param> 
    public static byte[] ReadFully(System.IO.Stream stream, long initialLength) 
    { 
     // reset pointer just in case 
     stream.Seek(0, System.IO.SeekOrigin.Begin); 

     // If we've been passed an unhelpful initial length, just 
     // use 32K. 
     if (initialLength < 1) 
     { 
      initialLength = 32768; 
     } 

     byte[] buffer = new byte[initialLength]; 
     int read = 0; 

     int chunk; 
     while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) 
     { 
      read += chunk; 

      // If we've reached the end of our buffer, check to see if there's 
      // any more information 
      if (read == buffer.Length) 
      { 
       int nextByte = stream.ReadByte(); 

       // End of stream? If so, we're done 
       if (nextByte == -1) 
       { 
        return buffer; 
       } 

       // Nope. Resize the buffer, put in the byte we've just 
       // read, and continue 
       byte[] newBuffer = new byte[buffer.Length * 2]; 
       Array.Copy(buffer, newBuffer, buffer.Length); 
       newBuffer[read] = (byte)nextByte; 
       buffer = newBuffer; 
       read++; 
      } 
     } 
     // Buffer is now too big. Shrink it. 
     byte[] ret = new byte[read]; 
     Array.Copy(buffer, ret, read); 
     return ret; 
    } 
+0

Ho modificato la mia domanda! Guarda! :-) – AndreMiranda

+0

Thaaaaaanks un sacco cottsak! Ha funzionato!! :-) E ho anche cambiato in un file il risultato, per essere più MVC! -) – AndreMiranda

+0

@AndreMiranda: hi 5 ;-) –

2

Sì, ma non è possibile salvarlo in un flusso. Uno stream non contiene dati, è solo il mezzo per accedere alla memoria reale.

Ottenere i dati come una matrice di byte, quindi è possibile inserirlo in una variabile di sessione, salvarlo come file e inviarlo come risposta.

Utilizzare un BinaryReader per ottenere i dati dal flusso di input a un array di byte:

byte[] data; 
using (BinaryReader reader = new BinaryReader(uploadedFile.InputStream)) { 
    data = reader.ReadBytes((int) uploadedFile.InputStream.Length); 
} 

(Edit: Modificato da uno StreamReader per un BinaryReader)

+0

data = reader.ReadToEnd() non funzionerà perché ReadToEnd() restituisce una stringa – AndreMiranda

+0

@AndreMiranda: fare riferimento alla mia modifica sopra. la lettura del flusso può essere difficile –

+0

@AndreMiranda: Sì, hai ragione. Basta usare un BinaryReader invece di uno StreamReader. Vedi la mia modifica sopra. – Guffa