CÔNG NGHỆ THÔNG TIN >> BÀI VIẾT CHỌN LỌC

Tích hợp trình soạn thảo CKEditor và trình quản lý tệp tin elFinder trong Asp.Net Core MVC 6

Đăng lúc: 08:26 PM - 29/12/2023 bởi Charles Chung - 608

Trong bài này tôi sẽ hướng dẫn các bạn tích hợp thư viện CKEditor để soạn thảo nội dung web, thư viện elFinder để quản lý tệp tin trên web, bài demo trên ASP.NET Core MVC 6

Giới thiệu

CKEditor (còn gọi là FCKeditor) là một trình soạn thảo mã nguồn mở theo kiểu WYSIWYG (tay làm - mắt thấy) của CKSource. Chương trình này có thể tích hợp vào các website mà không cần cài đặt. Phiên bản đầu tiên được phát hành năm 2003 và đến nay được rất nhiều người sử dụng, từ phiên bản 4.x trở đi muốn sử dụng người dùng phải bỏ chi phí mới có thể sử dụng, tuy nhiên các phiên bản từ 3.x trở về trước thì vẫn miễn phí. Trong bài này chúng ta sẽ sử dụng phiên bản 3.x

elFinder là một mã nguồn mở tích hợp cho web để quản lý tệp được viết bằng Javascript và sử dụng thư viện Jquery UI. Nó cung cấp bộ thư viện có thể tích hợp cho nhiều framework và nhiều trình soạn thảo web như ckeditor, tinymce giúp người dùng một trải nghiệm đơn giản, sáng tạo và đầy tiện lợi. Trong bài này tôi hướng dẫn các bạn tích hợp elFinder vào CKEditor (hoàn toàn miễn phí nha).

Cấu hình và sử dụng CKEditor và elFinder trong ASP.NET Core MVC 6

Bước 1: Tạo project ASP.NET Core MVC 6 đặt tên "AspNetCore6CkEditorAndELFinder"

Bước 2: Tải tài nguyên ckeditor-elfinder-jqueryui về và giải nén ra

  • Copy vào thư mục ckeditor và elfinder vào wwwroot của project
  • Copy thư mục jqueryui vào wwwroot/lib
  • Tạo thêm 1 thư mục files trong wwwroot để elFinder quản lý, xem hình cấu trúc project dưới nhé.

Bước 3: Vào menu Tools/Nuget Package Management/Package Manager Console và cài elFinder bản 1.3.6 bằng lệnh: NuGet\Install-Package elFinder.NetCore -Version 1.3.6

Cấu trúc project sau khi cài

Bước 4: Code logic

  • Trong thư mục Controllers tạo  Controller có tên "FileManagerController" bổ sung code theo gợi ý sau

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    [Route("quan-ly-tep-tin")]
    public class FileManagerController : Controller
    {
        readonly IWebHostEnvironment _env;
        public FileManagerController(IWebHostEnvironment env) => _env = env;
        [Route("connector")]
        public async Task<IActionResult> Connector()
        {
            var connector = GetConnector();
            var result = await connector.ProcessAsync(Request);
            return result;
        }
        // Hiển thị ảnh thumbnail
        // /el-finder-file-system/thumb
        [Route("thumb/{hash}")]
        public async Task<IActionResult> Thumbs(string hash)
        {
            var connector = GetConnector();
            return await connector.GetThumbnailAsync(HttpContext.Request, HttpContext.Response, hash);
        }
        private Connector GetConnector()
        {
            // Thư mục gốc lưu trữ là wwwwroot/files (đảm bảo có tạo thư mục này)
            string pathroot = "files";
            var driver = new FileSystemDriver();
            string absoluteUrl = UriHelper.BuildAbsolute(Request.Scheme, Request.Host);
            var uri = new Uri(absoluteUrl);
            // .. ... wwwwroot/files
            string rootDirectory = Path.Combine(_env.WebRootPath, pathroot);
            // https://localhost:5001/files/
            string url = $"{uri.Scheme}://{uri.Authority}/{pathroot}/";
            string urlthumb = $"{uri.Scheme}://{uri.Authority}/quan-ly-tep-tin/thumb/";
            var root = new RootVolume(rootDirectory, url, urlthumb)
            {
                //IsReadOnly = !User.IsInRole("Administrators")
                IsReadOnly = false, // Can be readonly according to user's membership permission
                IsLocked = false, // If locked, files and directories cannot be deleted, renamed or moved
                Alias = "files", // Beautiful name given to the wwwroot/files folder
                                 //MaxUploadSizeInKb = 2048, // Limit imposed to user uploaded file <= 2048 KB
                                 //LockedFolders = new List<string>(new string[] { "Folder1" }
                ThumbnailSize = 100,
            };
            driver.AddRoot(root);
            return new Connector(driver)
            {
                // This allows support for the "onlyMimes" option on the client.
                MimeDetect = MimeDetectOption.Internal
            };
        }
    }

 

  • Trong thư mục Models tạo  lớp có tên "Blog" bổ sung code theo gợi ý sau

 

1
2
3
4
5
6
7
8
    public class Blog
    {
        public string BlogId { get; set; } = Guid.NewGuid().ToString();
        public string Title { get; set; } = string.Empty;
        public string Description { get; set;} = string.Empty;
        public string Picture { get; set; } = string.Empty;
        public DateTime CreateDate { get; set; } = DateTime.Now;
    }

 

  • Mở tệp HomeController.cs và bổ sung code theo gọi ý bên dưới

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        //khai báo biến chứa thông tin tạm thời khi tạo mới 1 blog
        private static Blog? temp;
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
        public IActionResult Index()
        {
            return View();
        }
        //đáp ứng  hành động tạo blog
        [HttpGet]
        public IActionResult CreateBlog()
        {
            return View();
        }
        //đáp hành động submit blog
        [HttpPost]
        public IActionResult CreateBlog(Blog blog)
        {
           temp = blog;
           return RedirectToAction(nameof(Details));
        }
        //đáp ứng hành động hiển thị thông tin blog sau khi submit
        public IActionResult Details()
        {
            return View(temp);
        }
        public IActionResult Privacy()
        {
            return View();
        }
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }

 

  • Mở tệp Views/Shared/_Layout.cshtml bổ sung thêm 1 menu theo code gợi ý sau
1
2
3
<li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="CreateBlog">Tạo blog</a>
</li>

 

  • Tạo view có tên "CreateBlog.cshtml" trong thư mục Views/Home và code theo gợi ý sau

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
@model AspNetCore6CkEditorAndELFinder.Models.Blog
@{
    ViewData["Title"] = "Tạo mới bài viết";
}
<h1>Tạo mới bài viết</h1>
<hr />
<div class="row">
    <div class="col-md-12">
        <form asp-action="CreateBlog">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Description" class="control-label"></label>
                <textarea asp-for="Description" class="form-control"></textarea>
                <span asp-validation-for="Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Picture" class="control-label"></label>
                <input asp-for="Picture" class="form-control" />
                <div>
                    <img src="" id="PictureView" width="100" />
                </div>
                <button type="button" id="btnBrowse">Chọn ảnh</button>
                <span asp-validation-for="Picture" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Lưu" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-action="Index">Quay lại</a>
</div>
@section Styles{
    <link rel="stylesheet" href="~/lib/jqueryui/themes/base/theme.css" />
    <link rel="stylesheet" href="~/lib/jqueryui/themes/base/jquery-ui.css" />
    <link rel="stylesheet" href="~/elfinder/css/elfinder.full.css" />
    <link rel="stylesheet" href="~/elfinder/css/theme.min.css" />
    <link rel="stylesheet" href="~/elfinder-material-theme/Material/css/theme-gray.css" />
}
@section Scripts{
    <script type="text/javascript" src="~/lib/jqueryui/jquery-ui.min.js"></script>
    <script src="~/elfinder/js/elfinder.min.js"></script>
    <script src="~/ckeditor/ckeditor.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            if ($('#btnBrowse') != undefined) {
                $('#btnBrowse').click(function () {
                    var fm = $('<div id="elfinder" />').dialogelfinder({
                        url: '/quan-ly-tep-tin/connector',
                        baseUrl: "/elfinder/",
                        width: 840,
                        height: 450,
                        destroyOnClose: true,
                        title: 'Quản lý tệp tin',
                        getFileCallback: function (files, fm) {
                            $('#Picture').val('/' + files[0].path);
                            $('#PictureView').attr('src', '/' + files[0].path);
                        },
                        commandsOptions: {
                            getfile: {
                                multiple: true,
                                oncomplete: 'close',
                                folders: false
                            }
                        }
                    }).dialogelfinder('instance');
                })
            }
        });
        CKEDITOR.replace('Description');
    </script>
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

 

  • Tạo view có tên "Details.cshtml" trong thư mục Views/Home và code theo gợi ý sau

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@model Blog;
<h1>Thông tin bài viết</h1>
<p>Mã số: @Model.BlogId</p>
<p>Tiêu đề: @Model.Title</p>
<p>Ngày tạo: @Model.CreateDate.ToString("dd/MM/yyyy hh:mm:ss tt")</p>
<p>Ảnh <img src="@Model.Picture" width="200" /></p>
<p>Nội dung</p>
<div>
    @Html.Raw(Model.Description)
</div>

 

Bước 5: Chạy và kiểm tra kết quả

  • Màn hình tạo Blog

  • Màn hình kích vào nút chọn ảnh

  • Màn hình sau khi chọn ảnh xong

  • Màn hình sau khi lưu

Video đang quay....

Link tải source code

thay lời cảm ơn!

QUẢNG CÁO - TIẾP THỊ