ASP.NET MVC5 網站開發實踐(二) Member區域 - 添加文章

上次把架構作好了,此次作添加文章。添加文章涉及附件的上傳管理及富文本編輯器的使用,早添加文章時一併實現。javascript

要點:php

  • 富文本編輯器採用KindEditor。功能很強大,國人開發,LGPL開源,本身人的好東西沒有理由不支持。
  • 附件的上傳一樣基於KindEditor實現,能夠上傳圖片,flash,影音,文件等。

 

目錄

ASP.NET MVC5 網站開發實踐 - 概述html

ASP.NET MVC5 網站開發實踐(一) - 項目框架java

ASP.NET MVC5 網站開發實踐(一) - 框架(續) 模型、數據存儲、業務邏輯ajax

ASP.NET MVC5 網站開發實踐(二) - 用戶部分(1)用戶註冊數據庫

ASP.NET MVC5 網站開發實踐(二) - 用戶部分(2)用戶登陸、註銷json

ASP.NET MVC5 網站開發實踐(二) - 用戶部分(3)修改資料、修改密碼瀏覽器

ASP.NET MVC5 網站開發實踐(二) Member區域 - 文章管理架構服務器

 

 

1、添加文章

一、KindEditor富文本編輯器cookie

到官方網站http://kindeditor.net/down.php下載最新版本,解壓後把代碼複製到項目的Scripts文件夾下。

image

二、添加界面的顯示。

在ArticleController中添加Add 方法

/// <summary>
        /// 添加文章
        /// </summary>
        /// <returns>視圖頁面</returns>
        public ActionResult Add()
        {
            return View();
        }

右鍵添加Article的強類型視圖,代碼以下

@section scripts{
    <script type="text/javascript" src="~/Scripts/KindEditor/kindeditor-min.js"></script>
    <script type="text/javascript">
        //編輯框
        KindEditor.ready(function (K) {
            window.editor = K.create('#Content', {
                height: '500px'
         });
        });
    </script>
}

@model Ninesky.Models.Article
@using (Html.BeginForm())
{ @Html.AntiForgeryToken()
    <div class="form-horizontal" role="form">
        <h4>添加文章</h4>
        <hr />
        @Html.ValidationSummary(true)
        <div class="form-group">
            <label class="control-label col-sm-2" for="CommonModel_CategoryID">欄目</label>
            <div class="col-sm-10">
                <input id="CommonModel_CategoryID" name="CommonModel.CategoryID" data-options="url:'@Url.Action("JsonTree", "Category", new { model="Article" })'" class="easyui-combotree" style="height: 34px; width: 280px;" />
                       @Html.ValidationMessageFor(model => model.CommonModel.CategoryID)</div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.CommonModel.Title, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                @Html.TextBoxFor(model => model.CommonModel.Title, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.CommonModel.Title)
            </div>
        </div>


        <div class="form-group">
            @Html.LabelFor(model => model.Author, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                @Html.TextBoxFor(model => model.Author, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Author)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Source, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                @Html.TextBoxFor(model => model.Source, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Source)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Intro, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                @Html.TextAreaFor(model => model.Intro, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Intro)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Content, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                @Html.EditorFor(model => model.Content)
                @Html.ValidationMessageFor(model => model.Content)
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.CommonModel.DefaultPicUrl, new { @class = "control-label col-sm-2" })
            <div class="col-sm-10">
                <img id="imgpreview" class="thumbnail" src="" />
                @Html.HiddenFor(model => model.CommonModel.DefaultPicUrl)
                <a id="btn_picselect" class="easyui-linkbutton">選擇…</a>
                @Html.ValidationMessageFor(model => model.CommonModel.DefaultPicUrl)
            </div>
        </div>

        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <input type="submit" value="添加" class="btn btn-default" />
            </div>
        </div>
    </div>
}

效果如圖image

三、後臺接受的處理。

[ValidateInput(false)]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Add(Article article)
        {
            if(ModelState.IsValid)
            {
                //設置固定值
                article.CommonModel.Hits = 0;
                article.CommonModel.Inputer = User.Identity.Name;
                article.CommonModel.Model = "Article";
                article.CommonModel.ReleaseDate = System.DateTime.Now;
                article.CommonModel.Status = 99;
                article = articleService.Add(article);
                if (article.ArticleID > 0)
                {
                    return View("AddSucess", article);
                }
            }
            return View(article);
        }

在作架構的時候DAL、BLL的base類裏有Add方法,咱們能夠直接使用ArticleService.Add方法添加到數據庫

添加文章功能就實現了,可是不能上傳附件,不能選擇首頁圖片,不能刪除多餘的附件。下面就來實現附件功能。

2、附件上傳

目標能夠上傳附件(圖片,文件等),文件保存到上傳目錄中,且數據庫中保存相應記錄,能夠瀏覽文件列表,未使用的附件能夠刪除記錄。

1、添加附件

在AttachmentController添加Upload()方法,方法方法把文件寫入磁盤中把附件的記錄也保存到數據庫中,中間會用到讀取配置文件,見《.Net MVC 網站中配置文件的讀寫》。

/// <summary>
        /// 上傳附件
        /// </summary>
        /// <returns></returns>
        public ActionResult Upload()
        {
            var _uploadConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~").GetSection("UploadConfig") as Ninesky.Models.Config.UploadConfig;
            //文件最大限制
            int _maxSize = _uploadConfig.MaxSize;
            //保存路徑
            string _savePath;
            //文件路徑
            string _fileParth = "~/" + _uploadConfig.Path + "/";
            //文件名
            string _fileName;
            //擴展名
            string _fileExt;
            //文件類型
            string _dirName;
            //容許上傳的類型
            Hashtable extTable = new Hashtable();
            extTable.Add("image", _uploadConfig.ImageExt);
            extTable.Add("flash", _uploadConfig.FileExt);
            extTable.Add("media", _uploadConfig.MediaExt);
            extTable.Add("file", _uploadConfig.FileExt);
            //上傳的文件
            HttpPostedFileBase _postFile = Request.Files["imgFile"];
            if (_postFile == null) return Json(new { error = '1', message = "請選擇文件" });
            _fileName = _postFile.FileName;
            _fileExt = Path.GetExtension(_fileName).ToLower();
            //文件類型
            _dirName = Request.QueryString["dir"];
            if (string.IsNullOrEmpty(_dirName))
            {
                _dirName = "image";
            }
            if (!extTable.ContainsKey(_dirName)) return Json(new { error = 1, message = "目錄類型不存在" });
            //文件大小
            if (_postFile.InputStream == null || _postFile.InputStream.Length > _maxSize) return Json(new { error = 1, message = "文件大小超過限制" });
            //檢查擴展名
            if (string.IsNullOrEmpty(_fileExt) || Array.IndexOf(((string)extTable[_dirName]).Split(','), _fileExt.Substring(1).ToLower()) == -1) return Json(new { error = 1, message = "不容許上傳此類型的文件。 \n只容許" + ((String)extTable[_dirName]) + "格式。" });
            _fileParth += _dirName + "/" + DateTime.Now.ToString("yyyy-MM") + "/";
            _savePath = Server.MapPath(_fileParth);
            //檢查上傳目錄
            if (!Directory.Exists(_savePath)) Directory.CreateDirectory(_savePath);
            string _newFileName = DateTime.Now.ToString("yyyyMMdd_hhmmss") + _fileExt;
             _savePath += _newFileName;
             _fileParth += _newFileName;
            //保存文件
            _postFile.SaveAs(_savePath);
            //保存數據庫記錄
            attachmentService.Add(new Attachment() { Extension = _fileExt.Substring(1), FileParth = _fileParth, Owner = User.Identity.Name, UploadDate = DateTime.Now, Type = _dirName });
            return Json(new { error = 0, url = Url.Content(_fileParth) });
        }

2、查詢附件列表

打開InterfaceAttachmentService接口,添加兩個方法,都進行了註釋比較容易理解,直接上代碼。

/// <summary>
        ///  查找附件列表
        /// </summary>
        /// <param name="modelID">公共模型ID</param>
        /// <param name="owner">全部者</param>
        /// <param name="type">類型</param>
        /// <returns></returns>
      IQueryable<Models.Attachment> FindList(Nullable<int> modelID, string owner, string type);
      /// <summary>
      /// 查找附件列表
      /// </summary>
      /// <param name="modelID">公共模型ID</param>
      /// <param name="owner">全部者</param>
      /// <param name="type">全部者</param>
      /// <param name="withModelIDNull">包含ModelID爲Null的</param>
      /// <returns></returns>
      IQueryable<Models.Attachment> FindList(int modelID, string owner, string type,bool withModelIDNull);

AttachmentService中寫現實代碼

public IQueryable<Models.Attachment> FindList(Nullable<int> modelID, string owner, string type)
        {
            var _attachemts = CurrentRepository.Entities.Where(a => a.ModelID == modelID);
            if (!string.IsNullOrEmpty(owner)) _attachemts = _attachemts.Where(a => a.Owner == owner);
            if (!string.IsNullOrEmpty(type)) _attachemts = _attachemts.Where(a => a.Type == type);
            return _attachemts;
        }

        public IQueryable<Models.Attachment> FindList(int modelID, string owner, string type, bool withModelIDNull)
        {
            var _attachemts = CurrentRepository.Entities;
            if (withModelIDNull) _attachemts = _attachemts.Where(a => a.ModelID == modelID || a.ModelID == null);
            else _attachemts = _attachemts.Where(a => a.ModelID == modelID);
            if (!string.IsNullOrEmpty(owner)) _attachemts = _attachemts.Where(a => a.Owner == owner);
            if (!string.IsNullOrEmpty(type)) _attachemts = _attachemts.Where(a => a.Type == type);
            return _attachemts;
        }

因爲KindEditor文件管理須要從服務器獲取json格式文件列表,在Ninesky.Web.Areas.Member.Models中單獨給列表格式寫個視圖模型。AttachmentManagerViewModel

namespace Ninesky.Web.Areas.Member.Models
{
    /// <summary>
    /// KindEditor文件管理中文件視圖模型
    /// <remarks>
    /// 建立:2014.03.09
    /// </remarks>
    /// </summary>
    public class AttachmentManagerViewModel
    {
        public bool is_dir{get;set;}
        public bool has_file {get;set;}
        public int filesize {get;set;}
        public bool is_photo{get;set;}
        public string filetype{get;set;}
        public string filename{get;set;}
        public string datetime { get; set; }
    }
}

在AttachmentController添加返回文件列表的方法FileManagerJson。方法供KindEditor的文件管理器調用

/// <summary>
        /// 附件管理列表
        /// </summary>
        /// <param name="id">公共模型ID</param>
        /// <param name="dir">目錄(類型)</param>
        /// <returns></returns>
        public ActionResult FileManagerJson(int? id ,string dir)
        {
            Models.AttachmentManagerViewModel _attachmentViewModel;
            IQueryable<Attachment> _attachments;
            //id爲null,表示是公共模型id爲null,此時查詢數據庫中沒有跟模型對應起來的附件列表(以上傳,但上傳的文章……還未保存)
            if (id == null) _attachments = attachmentService.FindList(null, User.Identity.Name, dir);
            //id不爲null,返回指定模型id和id爲null(新上傳的)附件了列表
            else _attachments = attachmentService.FindList((int)id, User.Identity.Name, dir, true);
            //循環構造AttachmentManagerViewModel
            var _attachmentList = new List<Models.AttachmentManagerViewModel>(_attachments.Count());
            foreach(var _attachment in _attachments)
            {
                _attachmentViewModel = new Models.AttachmentManagerViewModel() { datetime = _attachment.UploadDate.ToString("yyyy-MM-dd HH:mm:ss"), filetype = _attachment.Extension, has_file = false, is_dir = false, is_photo = _attachment.Type.ToLower() == "image" ? true : false, filename = Url.Content(_attachment.FileParth) };
                FileInfo _fileInfo = new FileInfo(Server.MapPath(_attachment.FileParth));
                _attachmentViewModel.filesize = (int)_fileInfo.Length;
                _attachmentList.Add(_attachmentViewModel);
            }
            return Json(new { moveup_dir_path = "", current_dir_path = "", current_url = "", total_count = _attachmentList.Count, file_list = _attachmentList },JsonRequestBehavior.AllowGet);
        }

三、爲圖片建立縮略圖

把建立縮略圖的方法寫着Common項目中

在Ninesky.Common的Picture類中添加方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Security.Cryptography;

namespace Ninesky.Common
{
    /// <summary>
    /// 圖片相關
    /// <remarks>
    /// 建立:2014.02.11
    /// </remarks>
    /// </summary>
    public class Picture
    {
        /// <summary>
        /// 建立縮略圖
        /// </summary>
        /// <param name="originalPicture">原圖地址</param>
        /// <param name="thumbnail">縮略圖地址</param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns>是否成功</returns>
        public static bool CreateThumbnail(string originalPicture, string thumbnail, int width, int height)
        {
            //原圖
            Image _original = Image.FromFile(originalPicture);
            // 原圖使用區域
            RectangleF _originalArea = new RectangleF();
            //寬高比
            float _ratio = (float)width/height;
            if(_ratio > ((float)_original.Width/_original.Height))
            {
                _originalArea.X =0;
                _originalArea.Width = _original.Width;
                _originalArea.Height = _originalArea.Width / _ratio;
                _originalArea.Y = (_original.Height - _originalArea.Height) / 2;
            }
            else
            {
                _originalArea.Y = 0;
                _originalArea.Height = _original.Height;
                _originalArea.Width = _originalArea.Height * _ratio;
                _originalArea.X = (_original.Width - _originalArea.Width) / 2;
            }
            Bitmap _bitmap = new Bitmap(width, height);
            Graphics _graphics = Graphics.FromImage(_bitmap);
            //設置圖片質量
            _graphics.InterpolationMode = InterpolationMode.High;
            _graphics.SmoothingMode = SmoothingMode.HighQuality;
            //繪製圖片
            _graphics.Clear(Color.Transparent);
            _graphics.DrawImage(_original, new RectangleF(0, 0, _bitmap.Width, _bitmap.Height), _originalArea, GraphicsUnit.Pixel);
            //保存
            _bitmap.Save(thumbnail);
            _graphics.Dispose();
            _original.Dispose();
            _bitmap.Dispose();
            return true;
        }
    }
}

在AttachmentController添加生成縮略圖的action

/// <summary>
        /// 建立縮略圖
        /// </summary>
        /// <param name="originalPicture">原圖地址</param>
        /// <returns>縮略圖地址。生成失敗返回null</returns>
        public ActionResult CreateThumbnail(string originalPicture)
        {
            //原圖爲縮略圖直接返回其地址
            if (originalPicture.IndexOf("_s") > 0) return Json(originalPicture);
            //縮略圖地址
            string _thumbnail = originalPicture.Insert(originalPicture.LastIndexOf('.'), "_s");
            //建立縮略圖
            if (Common.Picture.CreateThumbnail(Server.MapPath(originalPicture), Server.MapPath(_thumbnail), 160, 120))
            {
                //記錄保存在數據庫中
                attachmentService.Add(new Attachment(){ Extension= _thumbnail.Substring(_thumbnail.LastIndexOf('.')+1), FileParth="~"+_thumbnail, Owner= User.Identity.Name, Type="image", UploadDate= DateTime.Now});
                return Json(_thumbnail);
            }
            return Json(null);
        }

3、整合

添加和上傳附件都作好了,如今把他們整合到一塊兒,咱們就能夠上傳附件了。

打開Add視圖,在建立KindEditor位置添加腳本

image

如今打開瀏覽器就能夠上傳和管理附件了

image

image

添加文章的最後一個字段是文章的默認首頁圖片,我但願點擊選擇按鈕,能夠在已上傳中選擇圖片,並建立縮略圖。

那麼在Add視圖裏再彈出一個文件空間讓用戶選擇已上傳的文件,用戶選擇後講選擇的地址發送到服務器建立縮略圖,並返回縮略圖地址,而後將地址複製給隱藏表單,CommonModel_DefaultPicUrl,同事複製個<img />的src屬性用來顯示圖片。Js代碼以下:

//首頁圖片
            var editor2 = K.editor({
                fileManagerJson: '@Url.Action("FileManagerJson", "Attachment")'
            });
            K('#btn_picselect').click(function () {
                editor2.loadPlugin('filemanager', function () {
                    editor2.plugin.filemanagerDialog({
                        viewType: 'VIEW',
                        dirName: 'image',
                        clickFn: function (url, title) {
                            var url;
                            $.ajax({
                                type: "post",
                                url: "@Url.Action("CreateThumbnail", "Attachment")",
                                data: { originalPicture: url },
                                async: false,
                                success: function (data) {
                                    if (data == null) alert("生成縮略圖失敗!");
                                    else {
                                        K('#CommonModel_DefaultPicUrl').val(data);
                                        K('#imgpreview').attr("src", data);
                                    }
                                    editor2.hideDialog();
                                }
                            });
                        }
                    });
                });
            });

看下效果

image

在保存文章的action中刪除未使用的附件

image

完整的Add方法代碼

[ValidateInput(false)]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Add(Article article)
        {
            if(ModelState.IsValid)
            {
                //設置固定值
                article.CommonModel.Hits = 0;
                article.CommonModel.Inputer = User.Identity.Name;
                article.CommonModel.Model = "Article";
                article.CommonModel.ReleaseDate = System.DateTime.Now;
                article.CommonModel.Status = 99;
                article = articleService.Add(article);
                if (article.ArticleID > 0)
                {
                    //附件處理
                    InterfaceAttachmentService _attachmentService = new AttachmentService();
                    //查詢相關附件
                    var _attachments = _attachmentService.FindList(null, User.Identity.Name, string.Empty).ToList();
                    //遍歷附件
                    foreach(var _att in _attachments)
                    {
                        var _filePath = Url.Content(_att.FileParth);
                        //文章首頁圖片或內容中使用了該附件則更改ModelID爲文章保存後的ModelID
                        if ((article.CommonModel.DefaultPicUrl != null && article.CommonModel.DefaultPicUrl.IndexOf(_filePath) >= 0) || article.Content.IndexOf(_filePath) > 0)
                        {
                            _att.ModelID = article.ModelID;
                            _attachmentService.Update(_att);
                        }
                        //未使用改附件則刪除附件和數據庫中的記錄
                        else
                        {
                            System.IO.File.Delete(Server.MapPath(_att.FileParth));
                            _attachmentService.Delete(_att);
                        }
                    }
                    return View("AddSucess", article);
                }
            }
            return View(article);
        }

4、總結:

單純添加文章比較簡單,複雜點在上傳附件,瀏覽新添加的附件,刪除文章中未使用的附件及生成縮略圖上。KindEditor還支持批量上傳附件,因爲批量上傳使用的swfupload,在提交時flash沒傳輸cookie到服務器,沒法驗證用戶致使上傳失敗,暫時沒法使用批量上傳。

相關文章
相關標籤/搜索