ASP.NET MVC 使用 CKEditor + CKFinder 实现图片上传
CKFinder 是收费的,但是免费版功能比较全(只是不能删除图片有点不大方便)。
安装与配置主要是参照官方文档:CKFinder 3 ASP.NET Connector Documentation。
由于是使用 MVC 开发,直接通过 NuGet 安装对应的包( CKSource.CKFinder.Connector.Sample
)就行了。
install-package -id CKSource.CKFinder.Connector.Sample
通过这种方式安装,文件会放在 CKFinderScripts
目录下,为了跟参考文档一致,我把目录改成了 CKFinder
。
因为使用的 VS2012 开发的,解析到依赖 System.Net.Http
就出错了。
想要升级到最新版本的 System.Net.Http
需要下载新版的 NuGet,但是 VS2012 已经没有更新的 NuGet 了。这就很尴尬了~~
好在机器上还装了 VS2015,用 VS2015 打开项目,再次执行上面的 install-package
命令。
CKEditor 中要包含下面两个插件:
Upload Image
File Browser
Package 成功安装后 CKEditor 中要设置如下配置项:
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 都更新了一下,结果启动报错了!
{"创建 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 就好了。
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
之后打开页面报了另外一个错。
[A]System.Web.WebPages.Razor.Configuration.HostSection 无法强制转换为 [B]System.Web.WebPages.Razor.Configuration.HostSection。
也是由于 Web.config
未更新导致的。这个 Web.config
在 Views
目录下面。将 Razor
对应的版本号更新成 3.0.0.0
就好了。
<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
记录了错误日志,其内容如下:
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& 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& 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.config
及 Startup.cs
中设置了一套默认的配置项及配置代码。
只需要修改 Startup.cs
中的代码即可以实现我想要的功能了。
修改后的 Startup.SetupConnector
代码如下:
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}
,返回的路径是一样的。这样每个用户访问的时候就只能看到自己的文件了。