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;
}
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. ;) –
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