The uploaded and downloaded function is a very common function in programming and has a very wide application in program development. This article implements this function in the form of examples.
1. Overview
If you only have a Web Forms background and study MVC, I think your first experience may be that the server controls that once made your programming extremely pleasant are all gone. FileUpload is one of them, and the absence of this control has caused us some minor problems. This article mainly talks about how to upload files in MVC and then download the uploaded files from the server.
2. Implementation method
1. File upload
In Web Forms, when you drag a FileUpload control into the designer, you may not notice that an extra attribute enctype="multipart/form-data" will be added to the form tag in the generated HTML. The FileUpload control itself will be generated as <input type="file" />. In the MVC view, there are many ways to achieve the same effect. The first HTML is as follows:
<form action="/" method="post" enctype="multipart/form-data"> <input type="file" name="FileUpload1" /><br /> <input type="submit" name="Submit" value="Upload" /> </form>
Note that the form tag already includes the enctype tag, and the method attribute is set to "post". This setting is not much higher than that done by HTTP get when submitting the default. In the following way, using the () extension method, the same HTML as above will be generated:
<% using (("", "home", , new {enctype="multipart/form-data"})) {%> <input type="file" name="FileUpload1" /><br /> <input type="submit" name="Submit" value="Upload" /> <% }%>
Pay attention to the name attribute of the <input type="file"> tag, we will discuss it later
OK, now we can browse the local file and submit the file to the server side through the Upload submission button. The next step is to process the uploaded files on the server side. When using the fileUpload control, you can easily check whether the file is uploaded through the hasFile method of FileUpload. But it seems that it is not so convenient in MVC, you will be closer to the original HTTP, however, an extension method can handle these:
public static bool HasFile(this HttpPostedFileBase file) { return (file != null && > 0) ? true : false; }
When you see the code of the corresponding Controller class, you will find that the Request object exists as a property of the HttpRequestBase type. HttpReuqestBase is actually an encapsulation of HTTP requests, exposing many attributes, including Files collection (actually a collection of HttpFileCollectionBase). Each element in the collection is a collection of HttpPostedFileBase. The extension method is used to ensure that the uploaded file exists. In fact, this is consistent with how the() method works.
It's actually easy to use in Controller Action:
public class HomeController : Controller { public ActionResult Index() { foreach (string upload in ) { if (![upload].HasFile()) continue; string path = + "uploads/"; string filename = ([upload].FileName); [upload].SaveAs((path, filename)); } return View(); } }
2. Multiple files upload
Maybe you've thought earlier than I did about how to better use it as a collection. This means that it can accommodate more than just one file, but multiple. We change the View above to the following:
<% using (("", "home", , new {enctype="multipart/form-data"})) {%> <input type="file" name="FileUpload1" /><br /> <input type="file" name="FileUpload2" /><br /> <input type="file" name="FileUpload3" /><br /> <input type="file" name="FileUpload4" /><br /> <input type="file" name="FileUpload5" /><br /> <input type="submit" name="Submit" value="Upload" /> <% }%>
The Controller code has checked whether all file upload boxes have files, so even for multi-file uploads, we no longer need to modify the Controller code. Note that each <input type="file"> has a different name attribute. If you need to call one of them, for example, you need to refer to the third input box and just use: ["FileUpload3"].
3. Save it into the database
Before you yell at me "separation of concerns", I want to declare that the code below is only used as an illustration function. I put the code into the Controller action, but we all know that this is not good. The data access code should be placed in the data access layer of a part of the Model. However, the following code can only give you a more intuitive impression of how to store the uploaded files into the database. First, we need to create a data table (FileTest) and create a table: FileStore
CREATE TABLE [dbo].[FileStore]( [ID] [int] IDENTITY(1,1) NOT NULL, [FileContent] [image] NOT NULL, [MimeType] [nvarchar](50) NOT NULL, [FileName] [nvarchar](50) NOT NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
The FileContent field is an image data type, which is used to store files formed in binary data, and the Index Action is changed to:
public ActionResult Index() { foreach (string upload in ) { if (![upload].HasFile()) continue; string mimeType = [upload].ContentType; Stream fileStream = [upload].InputStream; string fileName = ([upload].FileName); int fileLength = [upload].ContentLength; byte[] fileData = new byte[fileLength]; (fileData, 0, fileLength); const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;"; using (var conn = new SqlConnection(connect)) { var qry = "INSERT INTO FileStore (FileContent, MimeType, FileName) VALUES (@FileContent, @MimeType, @FileName)"; var cmd = new SqlCommand(qry, conn); ("@FileContent", fileData); ("@MimeType", mimeType); ("@FileName", fileName); (); (); } } return View(); }
The modified code will loop through all uploaded files in the web page in a loop, and check whether the file has been added to <input type="file">. Then, extract 3 information from the file: file name, MIME type (file type), and binary stream in HTTP Request. Binary data is converted into a byte array and stored in the database as image data type. MIME type and file name are very important for users to extract files from databases.
4. Return the file in the database to the user:
How you transfer files to users depends on how you first store them. If you store files in the database, you will return the files to the user in a stream. If you store files on your hard drive, you only need to provide a hyperlink, or you can also use streaming. Whenever you need to send files to the browser in a stream, you use the overload of the File() method (rather than using the View() method we have been using before). There are 3 types of return types for the File() method: FilePathResult, FileContentResult and FileStreamResult. The first type is used to return files directly from disk; the second type is used to return the byte array to the client; and the third way to return the content of the stream object that has been generated and opened.
If you remember, we save the uploaded file into the database and store it in the FileContent field as a byte array. When it needs to be extracted, it will still be extracted as a byte array, which means we use File() overloading that returns FileContentResult. If we want to make the extracted file name more meaningful, we use an overload that accepts 3 parameters, three parameters are: byte array, MIME type, file name:
public FileContentResult GetFile(int id) { SqlDataReader rdr; byte[] fileContent = null; string mimeType = "";string fileName = ""; const string connect = @"Server=.\SQLExpress;Database=FileTest;Trusted_Connection=True;"; using (var conn = new SqlConnection(connect)) { var qry = "SELECT FileContent, MimeType, FileName FROM FileStore WHERE ID = @ID"; var cmd = new SqlCommand(qry, conn); ("@ID", id); (); rdr = (); if () { (); fileContent = (byte[])rdr["FileContent"]; mimeType = rdr["MimeType"].ToString(); fileName = rdr["FileName"].ToString(); } } return File(fileContent, mimeType, fileName); }
The easiest way to use this Action in View is to provide a hyperlink:
<a href="/GetFile/1">Click to get file</a>
If the image stored in the database is of the image type, unlike using a hyperlink, we get it by pointing to a <image> tag with the src attribute of the Controller action:
<img src="/GetFile/1" alt="My Image" />
Let's take a look at how simple it is to use FilePathResult (for extracting files from hard disk):
public FilePathResult GetFileFromDisk() { string path = + "uploads/"; string fileName = ""; return File(path + fileName, "text/plain", ""); }
And this can also be extracted using hyperlinks:
<a href="/GetFileFromDisk">Click to get file</a>
The last option FileStreamResult can also extract files from disk:
public FileStreamResult StreamFileFromDisk() { string path = + "uploads/"; string fileName = ""; return File(new FileStream(path + fileName, ), "text/plain", fileName); }
3. Supplement
What is the difference between FilePathResult and FileStreamResult? How should we choose? The main difference is that FilePathResult is used to write files to the Http output stream. This method does not buffer in server memory, so this is a good choice for sending large files. Their differences are very similar to the difference between DataReader and DataSet. At the same time, there is a bug in TransmitFile, which may cause the file to stop halfway to the client and even fail to be transmitted. And FileStreamResult is great in this regard. For example: Returns the chart image generated by the Chart control in memory, which does not require the image to be stored on disk.