BlueCat
BlueCat

Reputation: 763

Save image in sql database with razor (ASP .net MVC, Entity Framework)

I have a web application, where I want to save images in my database. In my Application.Web I have a folder called images. The uploaded images should be saved there. I want to save the images in my database as a path like \images\user1.jpg.

My Create.cshtml, which I've generated from the controller, looks like this:

@model Application.Dal.User
@using (Html.BeginForm()) {
    @Html.LabelFor(model => model.Picture)
    @Html.EditorFor(model => model.Picture)
    <input type="submit" value="Create" class="btn btn-default" />
}

Now it looks like this: enter image description here

The user would have to write a path in the textfield, which is obviously wrong. I want the user to select an image, like this:

<input type="file" name="image" accept="image/*" id="inputImage" value="model => model.Picture)"/>

Which turns the field into (Choose file): enter image description here

Well, thats what I want but how do I bind the selected image to model => model.Picture? I tried to put "model => model.Picture" in the value attribut of the input but it doesn't work. And if it would work, how can I make sure that the picture will be saved in the image folder?

I've looked at this page: https://forums.asp.net/t/2021506.aspx?Upload+audio+file+and+save+to+Database+in+ASP+NET+MVC+using+Codefirst+EF+ but this doesn't really explain how I can bind the picture to model => model.Picture.

Upvotes: 0

Views: 3574

Answers (2)

WhatsThePoint
WhatsThePoint

Reputation: 3635

I have actually done similar in a recent project.

In your view you can create an input tag of type file, which it looks like you have already done.

<input name="attachment" type="file">

Then in your controller, you can use the HttpPostedFileBase class to to pass it back to your code and save it.

[HttpPost]
public ActionResult Save(HttpPostedFileBase Attachment)
    {
        //do stuff
        SaveFile(Attachment)
    }

This method returns an enum which I will attach later on. I use an enum here in a similar effect to boolean except I can return more than two options.

public FileUploadState SaveFile(HttpPostedFileBase Attachment)
{
    if(Attachment != null)
    {
        try //attempt to save file to file system
        {
            var fileName = Path.GetFileName(Attachment.FileName);
                    //path as parameter can be changed to any desired valid path
            var path = Path.Combine((@"C:\Images"), fileName);
            Attachment.SaveAs(path);
            return FileUploadState.Uploaded;
        }
        catch //implement your own error handling here 
        {
            //error handling
            return FileUploadState.Failed;
        }
    }
    else
    {
        return FileUploadState.NoFileSelected;
    }
}

Enum: In computer programming, an enumerated type is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. More info on Enums

public enum fileUploadState
{
    Uploaded,
    NoFileSelected,
    Failed
}

Then to access these attachments, later on you can put this code in. Assuming you are reading filenames from a database.

In your controller:

readFileName = get filename from DB;
ViewBag.Attachment = readFileName;

In your view create a link for your user to click to open the attachment: target = "_blank" opens in a new tab

@Html.ActionLink("First Attachment: " + (string)ViewBag.Attachment, "DownloadAttachment", new { @FileName = (string)ViewBag.Attachment }, new { target = "_blank" })}

Back to your controller: Assuming you use the same Attachment class I created. I created the attachment class with a valid constructor to store useful information about the files, i.e. filenames, paths (incase different), byte content.

    public ActionResult DownloadAttachment(string FileName)
    {
        //path as parameter can be changed to wherever the file exists
        Attachment attach = new Attachment(FileName, @"C:\Images");
        //when attachment is created, byte content is read
        var cd = new System.Net.Mime.ContentDisposition
        {
            FileName = attach.FileName,
            Inline = true,
        };
        Response.AppendHeader("Content-Disposition", cd.ToString());
        //return content
        return File(attach.FileData, attach1.contentType);
    }

Attachment Class:

public class Attachment
{
    public string FileName { get; set; }
    public string FilePath { get; set; }
    public byte[] FileData { get; set; }
    public string contentType { get; set; }

    public Attachment(string fileName, string filePath)
    {
        this.FileName = fileName;
        this.FilePath = filePath + @"\"+ fileName ;
        this.FileData = System.IO.File.ReadAllBytes(FilePath);
        this.contentType = MimeMapping.GetMimeMapping(FilePath);
    }
}

Upvotes: 4

justiceorjustus
justiceorjustus

Reputation: 1965

The latter <input> method is the correct way to do it.

Razor would look like this:

@using (Html.BeginForm("YourAction", "YourController", null, FormMethod.Post, 
new { enctype = "multipart/form-data" }))
{    
    @Html.LabelFor(model => model.Picture)
    <input type="file" name="file" multiple="multiple" />
    <input type="submit" value="Save" />
}

The upload information is stored in the Request.Files on the POST.

You can access it with something like this:

List<string> picture = new List<string>();

for (int i = 0; i < Request.Files.Count; i++)
{
    HttpPostedFileBase file = Request.Files[i];

    if (file != null && file.ContentLength > 0)
    {
        var fileName = Path.GetFileName(file.FileName);
        var fileNameAndPath = Path.Combine(Server.MapPath("~/image/"), fileName);
        file.SaveAs(fileNameAndPath);
        viewModel.Picture = fileNameAndPath.ToString();
    }
}

db.SaveChanges();

Edit: Is Picture just the path of the picture? If so, let me know and I can update my code to reflect your model better.

Upvotes: 1

Related Questions