如何使用 C# 上传文件并将其保存到 Stream 以便进一步预览?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1653469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:43:00  来源:igfitidea点击:

How can I upload a file and save it to a Stream for further preview using C#?

c#asp.net-mvcsessionfile-uploadstream

提问by AndreMiranda

Is there a way to upload a file, save it to a Stream, this Stream I will save it temporarily in a Session and, at last, I will try to preview this uploaded file that is in this Session??

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

For example, a pdf file.

例如,pdf 文件。

Thanks!!

谢谢!!

EDITED

已编辑

Here's what I'm trying to do:

这是我想要做的:

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

And in another method, I'm doing this:

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

byte[] imageByte = null;

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

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

But nothing happends... my browser even opens a nem page to show the pdf file, but a window is shown saying that the file is not a pdf (or something like the file doesn't initiate with pdf, I didn't understand that)

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

采纳答案by Matt Kocaj

Sure is. I upload files (PDF/images) to my db in my app. My model object actually stores the file as a byte array but for other functions i have to convert to and from streams so im sure its just as easy to keep it in stream format.

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

Here are some code examples (copy n paste) from my app-

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

The Fileobject that i use to move files (PDFs / images) around:

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

..the PdfDoctype specifically for PDF files:

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

.. an example of an action that receives multi-part POST for file uploading:

.. 接收多部分 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 });
    }

Now you can see i pass the file object to my service here but you can choose to add it to the session and pass an id back to the 'preview' view for example.

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

Finally, here is a generic action i use to render files out to the client (you could have something similar render the files/stream from the 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);
    }

EDIT:
I noticed i need more samples to explain the above-

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

For the upload action above, the FileUploadResultDTOclass:

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

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

And the GetFilesFromRequestfunction:

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

And finally (i hope i have everything you need now) this ReadFullyfunction. It's not my design. I got it from the net - stream reading can be tricky. I find this function is the most successful way to completely read a stream:

最后(我希望我拥有你现在需要的一切)这个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;
    }

回答by Guffa

Yes, but you can't save it to a stream. A stream doesn't contain any data, it's just the means for accessing the actual storage.

是的,但您无法将其保存到流中。流不包含任何数据,它只是访问实际存储的方式。

Get the data as a byte array, then you can put it in a session variable, save it as a file, and send it as a response.

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

Use a BinaryReader to get the data from the input stream to a byte array:

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

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

(Edit: Changed from a StreamReader to a BinaryReader)

(编辑:从 StreamReader 更改为 BinaryReader)