Skip to content

ASP.NET MVC 使用 CKEditor + CKFinder 实现图片上传

CKFinder 是收费的,但是免费版功能比较全(只是不能删除图片有点不大方便)。

安装与配置主要是参照官方文档:CKFinder 3 ASP.NET Connector Documentation

由于是使用 MVC 开发,直接通过 NuGet 安装对应的包( CKSource.CKFinder.Connector.Sample )就行了。

powershell
install-package -id CKSource.CKFinder.Connector.Sample

通过这种方式安装,文件会放在 CKFinderScripts 目录下,为了跟参考文档一致,我把目录改成了 CKFinder

因为使用的 VS2012 开发的,解析到依赖 System.Net.Http 就出错了。

想要升级到最新版本的 System.Net.Http 需要下载新版的 NuGet,但是 VS2012 已经没有更新的 NuGet 了。这就很尴尬了~~

好在机器上还装了 VS2015,用 VS2015 打开项目,再次执行上面的 install-package 命令。

CKEditor 中要包含下面两个插件:

  1. Upload Image

  2. File Browser

Package 成功安装后 CKEditor 中要设置如下配置项:

cs
CKEDITOR.config.filebrowserBrowseUrl = "/ckfinder/ckfinder.html";
CKEDITOR.config.filebrowserImageBrowseUrl = "/ckfinder/ckfinder.html?type=Images";
CKEDITOR.config.filebrowserUploadUrl = "/ckfinder/connector?command=QuickUpload&type=Files";
CKEDITOR.config.filebrowserImageUploadUrl = "/ckfinder/connector?command=QuickUpload&type=Images";

之后又顺便把项目里所有显示有更新的 package 都更新了一下,结果启动报错了!

txt
{"创建 entityFramework 的配置节处理程序时出错: 未能加载文件或程序集“EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”或它的某一个依赖项。
找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040) (D:\\xxx\\Liujiajia.Me\\Liujiajia.Me\\web.config line 9)"}

项目使用了 EntityFramework,刚刚从 5.0.0 更新到了 6.1.3,但是 web.config 中的配置没有改,从而导致了这个错误。

Web.config 中的 Version 原本是 5.0.0.0,改成 6.0.0.0 就好了。

xml
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />

之后打开页面报了另外一个错。

txt
[A]System.Web.WebPages.Razor.Configuration.HostSection 无法强制转换为 [B]System.Web.WebPages.Razor.Configuration.HostSection。

也是由于 Web.config 未更新导致的。这个 Web.configViews 目录下面。将 Razor 对应的版本号更新成 3.0.0.0 就好了。

xml
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
  <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>

之后再 CKEditor 中上传图片是出错,好在 CKFinder 记录了错误日志,其内容如下:

txt
Fatal | CKSource.CKFinder.Connector.Core.CommandHandler | 2017-03-31 16:01:57.4368 | An unknown error has occured during execution of FileUpload command.| System.MissingMethodException: 找不到方法:“Void ImageProcessor.Imaging.ResizeLayer..ctor(System.Drawing.Size, ImageProcessor.Imaging.ResizeMode, ImageProcessor.Imaging.AnchorPosition, Boolean, Single[], System.Nullable`1<System.Drawing.Size>, System.Collections.Generic.List`1<System.Drawing.Size>)”。
   在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandExecutor.<ExecuteAsync>d__9.MoveNext()
   在 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine&amp; stateMachine)
   在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandExecutor.ExecuteAsync(FileUploadParameters commandParameters, CancellationToken cancellationToken)
   在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandImpl.<ExecuteAsync>d__6.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   在 CKSource.CKFinder.Connector.Core.CommandHandler.<ExecuteAsync>d__12.MoveNext()    在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandExecutor.<ExecuteAsync>d__9.MoveNext()
   在 System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine&amp; stateMachine)
   在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandExecutor.ExecuteAsync(FileUploadParameters commandParameters, CancellationToken cancellationToken)
   在 CKSource.CKFinder.Connector.Core.Commands.FileUpload.FileUploadCommandImpl.<ExecuteAsync>d__6.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   在 CKSource.CKFinder.Connector.Core.CommandHandler.<ExecuteAsync>d__12.MoveNext()

这个居然是由于 ImageProcessor 包太新了导致了。新版的 ImageProcessor.Imaging.ResizeLayer 构造函数在最后多了一个 Point 参数,导致调用失败。把这个包的版本改成了 2.4.0 就好了。

更新到线上(服务器是放在阿里云上的)后图片可以上传,但是打开【浏览服务器文件】页面是空白。

看控制台是加载 CKFinder\lang\zh-cn.json 时出错,猜想是 MIME 设置导致的。

在阿里云的控制台里添加如下 MIME 设置:

关联扩展名:.json

内容类型 (MIME):application/x-javascript

再打开浏览服务器文件就正常了。


到这里位置上传图片的功能已经有了,但是所有用户默认的都是同一个目录,而我想要的功能是每个用户的文件应该是分开的,而且只能看到自己上传的文件。

CKSource.CKFinder.Connector.Sample Package 安装后,已经自动在 Web.configStartup.cs 中设置了一套默认的配置项及配置代码。

只需要修改 Startup.cs 中的代码即可以实现我想要的功能了。

修改后的 Startup.SetupConnector 代码如下:

cs
private static void SetupConnector(IAppBuilder builder)
{
    var allowedRoleMatcherTemplate = ConfigurationManager.AppSettings["ckfinderAllowedRole"];
    var authenticator = new RoleBasedAuthenticator(allowedRoleMatcherTemplate);

    var connectorFactory = new OwinConnectorFactory();
    var connectorBuilder = new ConnectorBuilder();
    var connector = connectorBuilder
        .LoadConfig()
        .SetAuthenticator(authenticator)
        .SetRequestConfiguration(
            (request, config) =>
            {
                config.LoadConfig();
                config.RemoveBackend("default");
                config.AddBackend("default", new LocalStorage(@"Uploads\userfiles\" + MySession.User.Id.ToString(), "/uploads/userfiles/" + MySession.User.Id.ToString()));
                var defaultBackend = config.GetBackend("default");

                var keyValueStoreProvider = new FileSystemKeyValueStoreProvider(defaultBackend);
                config.SetKeyValueStoreProvider(keyValueStoreProvider);
            })
        .Build(connectorFactory);

    builder.UseConnector(connector);
}

SetRequestConfiguration 中设置的代理方法在每次请求时都会执行,我们在这里将 Web.config 中配置的默认 Backend 移除掉,替换成我们自定义的 Backend

这里替换成了一个本地的 Backend,物理路径为 Uploads\userfiles\{用户ID},返回的路径是一样的。这样每个用户访问的时候就只能看到自己的文件了。