【问题标题】:How can I upload a file and save it to a Stream for further preview using C#?如何使用 C# 上传文件并将其保存到 Stream 以便进一步预览?
【发布时间】:2010-12-11 19:51:40
【问题描述】:

有没有办法上传一个文件,保存到一个Stream中,这个Stream我会暂时保存在一个Session中,最后我会尝试预览这个上传的文件在这个Session中??

例如,pdf 文件。

谢谢!!

已编辑

这是我想要做的:

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();

在另一种方法中,我正在这样做:

byte[] imageByte = null;

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

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

但是什么都没有发生...我的浏览器甚至打开了一个 nem 页面来显示 pdf 文件,但是显示一个窗口说该文件不是 pdf (或者类似文件不以 pdf 启动的东西,我没有不明白)

【问题讨论】:

  • 请参阅下面的示例,该示例将字节数组写入为 FileResult。它比 Response.BinaryWrite 更安全,因为您使用的是 aspnet mvc。此外,使用更好的流阅读器(请参阅下面的编辑)并逐步检查您的代码以验证上传的文件实际上是否已在会话中转储。 ;)
  • 您的代码的主要问题是您根本不会从输入流中读取数据。您正在创建一个大小合适的缓冲区,但您是从空内存流中读取到缓冲区而不是从输入流中读取的。你得到的只是一个充满零的数组,这显然不能用作 PDF 文件。

标签: c# asp.net-mvc session file-upload stream


【解决方案1】:

当然是。我在app 中将文件(PDF/图像)上传到我的数据库。我的模型对象实际上将文件存储为字节数组,但对于其他函数,我必须在流之间进行转换,所以我确信将其保持为流格式同样容易。

以下是我的应用程序中的一些代码示例(复制粘贴)-

File 用于移动文件(PDF/图像)的对象:

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; }
}

..PdfDoc 类型专门用于 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;
    }
}

.. 接收文件上传的多部分 POST 的操作示例:

    //
    // POST: /Announcements/UploadPdfToAnnouncement/ID
    [KsisAuthorize(Roles = "Admin, Announcements")]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult UploadPdfToAnnouncement(int ID)
    {
        FileManagerController.FileUploadResultDTO files =
            FileManagerController.GetFilesFromRequest((HttpContextWrapper)HttpContext);
        if (String.IsNullOrEmpty(files.ErrorMessage) && files.TotalBytes > 0)
        {
            // add SINGLE file to the announcement
            try
            {
                this._svc.AddAnnouncementPdfDoc(
                    this._svc.GetAnnouncementByID(ID),
                    new PdfDoc(files.Files[0]),
                    new User() { UserName = User.Identity.Name });
            }
            catch (ServiceExceptions.KsisServiceException ex)
            {
                // only handle our exceptions
                base.AddErrorMessageLine(ex.Message);
            }
        }

        // redirect back to detail page
        return RedirectToAction("Detail", "Announcements", new { id = ID });
    }

现在您可以在此处看到我将文件对象传递给我的服务,但您可以选择将其添加到会话并将 id 传递回“预览”视图。

最后,这是我用来将文件呈现给客户端的通用操作(您可以使用类似的东西从 Session 中呈现文件/流):

    //
    // 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);
    }

编辑:
我注意到我需要更多示例来解释上述内容-

对于上面的上传操作,FileUploadResultDTO 类:

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

还有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;
    }

最后(我希望我现在拥有你需要的一切)这个ReadFully 函数。这不是我的设计。我是从网上得到的——流式阅读可能很棘手。我发现这个函数是完全读取流的最成功的方法:

    /// <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;
    }

【讨论】:

  • Thaaaaaanks 很多 cottsak!有效!! :-) 而且我还把结果改成了一个文件,要更多的MVC! -)
  • @cottsak 太好了,但是如果我想显示上传文件的进度怎么办,只要在 mvc3 中可以使用任何东西都没有关系。希望你能做一个样品。谢谢
  • 不能那样做,瑞安,对不起。这使用每个浏览器的浏览器内置/本机上传功能。没有 API 可以取得进展(据我所知)。但是,Chrome 在本地上传期间确实会显示进度。
【解决方案2】:

是的,但您不能将其保存到流中。流不包含任何数据,它只是访问实际存储的手段。

以字节数组的形式获取数据,然后可以将其放入会话变量中,将其保存为文件,然后作为响应发送。

使用 BinaryReader 将输入流中的数据获取到字节数组中:

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

(编辑:从 StreamReader 更改为 BinaryReader)

【讨论】:

  • data = reader.ReadToEnd() 将不起作用,因为 ReadToEnd() 返回一个字符串
  • @AndreMiranda:参考我上面的编辑。流式读取可能很棘手
  • @AndreMiranda:是的,你是对的。只需使用 BinaryReader 而不是 StreamReader。请参阅上面的编辑。
  • 很棒的代码段。谢谢。在 IIS 上,使用 int.MaxValue 时出现内存不足异常。替换为 UploadedFile.InputStream.Length 效果很好
【解决方案3】:
byte[] data; 
using (Stream inputStream = PdfPath.InputStream) 
{ 
    MemoryStream memoryStream = inputStream as MemoryStream; 

    if (memoryStream == null) 
    { 
        memoryStream = new MemoryStream(); 
        inputStream.CopyTo(memoryStream); 
    } 

    data = memoryStream.ToArray(); 
}

【讨论】:

    猜你喜欢
    • 2013-03-30
    • 2012-11-17
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    相关资源
    最近更新 更多