5

我对 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 数据库的建议?

4

3 回答 3

8

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

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

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

因此,第一步是为Profile. 通常将其命名为ProfileViewModelor ProfileVM。然后,在此模型上,您将只添加Profile您将在视图中编辑或以其他方式与之交互的属性。然后,您实际上可以为特殊用途的视图函数添加不在数据库支持的模型上的其他属性,例如SelectList使用 with 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;
}

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

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

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 类型以匹配您的业务案例。

于 2013-03-18T20:04:35.760 回答
2

当我学习的时候,我用这个这个来开始上传文件。

我还建议在您的模型中将属性 Photo 设置为 byte[] 或类似的属性,以便更好地保存回您的数据库(我假设您使用的是 LINQ to SQL 或实体框架之类的东西)。

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

  1. 在您的控制器方法中有一个 HttpPostedFileBase 类型的参数并将其称为“照片”(因此它将从您的文件输入字段称为“照片”的视图中绑定)
  2. 在您的控制器方法中,检查参数是否为空,如果不是,您可以通过 HttpPostedFileBase 的 InputStream 属性将文件读入 MemoryStream 之类的东西
  3. 从 MemoryStream 中,您可以通过 ToArray() 方法获取 byte[]
  4. 将 byte[] 插入模型,然后将其保存到数据库

另外,记得在完成后处理 MemoryStream

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

HTH,弥敦道

于 2013-03-18T18:58:24.327 回答
0

在我的应用程序中,我将图像作为 base64string,因此,nvarchar(max) 是我使用的数据类型。这样做的好处是您可以将 JSON 对象中的 based64String 图像从网站传递或传递给网站。

将图像转换为 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

于 2015-05-06T07:38:40.790 回答