在MVC4中,如何将文件(图像)上传到属于我的域模型的SQL Server?

时间:2013-03-18 18:45:53

标签: c# asp.net sql-server asp.net-mvc file-upload

我对MVC比较陌生,我从来不必处理将文件(特别是图像)上传到SQL Server数据库的问题。说实话,我不知道我在这里做什么。

以下是我到目前为止的内容 - 这是我的域名模型(请注意我模型中的HttpPostedFileBase - 这就是我要上传的内容):

public class Profile
{
    [Key]
    public int Id { get; set; }

    [Required(ErrorMessage="Years of service is required")]
    [DisplayName("Years Service:")]
    public int YearsService { get; set; }

    [DataType(DataType.MultilineText)]
    [DisplayName("Notable Achivements:")]
    public string NotableAchivements { get; set; }

    [Required(ErrorMessage = "Technical skills are required")]
    [DataType(DataType.MultilineText)]
    [DisplayName("Technical Skills:")]
    public string TechnicalSkills { get; set; }

    [DisplayName("Upload Image: ")]
    public HttpPostedFileBase Photo { get; set; }

    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
}

这是我的观点:

@using (Html.BeginForm("Create", "Profiles", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="editor-label">
    @Html.LabelFor(model => model.YearsService)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.YearsService)
    @Html.ValidationMessageFor(model => model.YearsService)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.NotableAchivements)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.NotableAchivements)
    @Html.ValidationMessageFor(model => model.NotableAchivements)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.TechnicalSkills)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.TechnicalSkills)
    @Html.ValidationMessageFor(model => model.TechnicalSkills)
</div>

<input type="file" name="photo" />
<input type="submit" name="Submit" id="Submit" value="Upload" />
}

我希望有一些明显的事情表明我做错了。任何人都可以提供有关如何将简单文件上传到SQL Server数据库的建议吗?

3 个答案:

答案 0 :(得分:8)

首先,不要将图像保存到数据库中。网上有无数的“指南”,建议在MSSQL中使用字节数组或“图像”类型。数据库不用于二进制数据存储。当然,它们可以容纳二进制存储,但仅仅因为你可以使用AR-15射击自己,并不意味着你应该

相反,将图像存储在它所属的位置:文件系统。然后,只是在数据库中引用它的路径。

现在,处理所有这一切的最简单方法是使用视图模型。如果您还没有使用视图模型,那么现在是开始的好时机。通常,您不希望将实际的数据库支持的模型直接发送到视图。在形式上,恶意用户可能会遭受各种令人讨厌的数据篡改,即使在简单的视图中,您通常也会传递超出他们需要的数据。

因此,第一步是为Profile创建一个视图模型。通常将此命名为ProfileViewModelProfileVM。然后,在此模型上,您只会在视图中添加要编辑或以其他方式与之交互的Profile属性。然后,您实际上可以在数据库支持的模型上为特殊用途的视图函数添加其他属性 not ,例如SelectList用于DropDownListFor,所以你实际上可以保持强类型,而不是诉诸于ViewBag这样的事情。

对于您的图片,您需要两个字段。我通常会使用以下内容:

public string Photo { get; set; }
public HttpPostedFileBase PhotoUpload { get; set; }

在您的编辑/创建视图中,您只会引用PhotoUpload,这将是您的文件上传字段(尽管您可以使用Photo来显示当前设置的图片,如果您等)。

@Html.TextBoxFor(m => m.PhotoUpload, new { type = "file" })

然后,在控制器操作中处理发布的文件:

if (model.PhotoUpload.ContentLength > 0) {
    // A file was uploaded
    var fileName = Path.GetFileName(model.PhotoUpload.FileName);
    var path = Path.Combine(Server.MapPath(uploadPath), fileName);
    model.PhotoUpload.SaveAs(path);
    model.Photo = uploadPath + fileName;
}

uploadPath应该是您要存储上传图片的主目录相对路径,例如~/uploads/profile/photos。确保在尝试发布表单之前创建目录,或者添加一些逻辑来检查目录是否存在并在必要时创建它(这需要更高的服务器信任,因此在大多数安全性环境中都不理想一个很大的问题)。

然后,您只需要一些方法将视图模型中的数据映射回数据库支持的模型。您可以手动执行此操作,但使用AutoMapper之类的操作可以让您的生活更轻松。使用automapper(其中modelProfileViewModel的实例):

var profile = AutoMapper.Mapper.Map<Profile>(model);

或者,由于使用类似于个人资料的内容,您编辑现有模型比创建新模型更常见:

var profile = db.Profiles.Find(userId);
...
Automapper.Mapper.Map(model, profile);

在您的实际模型上,您将不会拥有PhotoUpload属性,只有Photo。您在视图模型上为Photo设置的路径将映射到您的模型的同名属性上,因此剩下的就是保存更新的配置文件。

此外,由于您正在谈论上传照片,因此您可能希望添加一些内容类型检查,以确保用户上传图片而不是像Word文档那样愚蠢的内容。在if (ModelState.IsValid)检查之前,请添加:

var validTypes = new[] { "image/jpeg", "image/pjpeg", "image/png", "image/gif" };
if (!validTypes.Contains(model.PhotoUpload.ContentType))
{
    ModelState.AddModelError("PhotoUpload", "Please upload either a JPG, GIF, or PNG image.");
}

根据需要更改mime类型以符合您的业务案例。

答案 1 :(得分:2)

在我学习时,我使用thisthis让我开始上传文件。

我还建议你在你的模型中使属性Photo一个byte []或类似的东西应该更好地保存回你的数据库(我假设你使用的是LINQ to SQL或Entity Framework)。

根据以上链接,我建议您通过以下方式处理控制器方法中的上传:

  1. 在控制器方法中使用HttpPostedFileBase类型的参数并将其命名为“photo”(因此它将从您的视图绑定文件输入字段称为“photo”)
  2. 在你的控制器方法中,检查参数是否为空,如果不是,你可以通过HttpPostedFileBase的InputStream属性将文件读入MemoryStream之类的东西
  3. 从MemoryStream中,您可以通过ToArray()方法获取byte []
  4. 将字节[]插入模型,然后将其保存到数据库
  5. 另外,记得在完成后丢弃MemoryStream

    我确信有更好的方法,但这些方法一直对我有用: - )

    HTH, 森

答案 2 :(得分:0)

在我的应用程序中,我将图像作为base64string,因此,nvarchar(max)是我使用的数据类型。这样做的优点是你可以将JSON对象中的64字符串图像从网站传递到网站。

将图片转换为based64string

public static string ToBased64String(this Image image, ImageFormat format)
{
  using (MemoryStream ms = new MemoryStream())
  {
    image.Save(ms, format);
    byte[] imageBytes = ms.ToArray();
    string based64String = Convert.ToBased64String(imageBytes);
    return based64String;

  }
}

然后你可以像这样调用你的方法

image.ToBased64String

将based64String转换为图像

public static ImageFromBased64String(string based64Image, string path)
{
  Image image = null;
  var bytes = Convert.FromBased64String(based64String);
  using (var fileStream = new FileStream(path, FileMode.Create))
  {
    fileStream.Write(bytes, 0, bytes.Length);
    fileStream.Flush();
    image = Image.FromStream(fileStream, true);
    return image;
  }
}

请参考我的回答

https://stackoverflow.com/a/30057680/1554116