官网地址:https://cksource.com/ckfinder
但是官网下载的源码,虽然免费,但是会有讨厌的版权信息显示,这里我已经去除了,唯一不足之处就是ckfinder的中文文件夹和中文文件上传会有异常,这是因为,我们的浏览器会自动对中文字符进行url编码,并且根据页面编码格式,所以我们处理这种情况可以使用js提供编码函数,自己encodeurl等等实现自定义编码,但是由于ckfinder的js看不太懂,所以就没有去改,有心的同学可以自己改改看
下载解压后,将,ckfinder文件夹直接拖到web项目的webcontent文件夹xia并将config.xml拖到web-inf下
具体配置我就不讲了
网上多得是,也可参考我的之前写的
http://blog.csdn.net/do_bset_yourself/article/details/51568576?locationNum=1&fps=1
如果直接配置成这样会,表示任何和都是上传和删除文件
<accessControl>
<role>*role>
<resourceType>*resourceType>
<folder>/folder>
<folderView>truefolderView>
<folderCreate>truefolderCreate>
<folderRename>truefolderRename>
<folderDelete>truefolderDelete>
<fileView>truefileView>
<fileUpload>truefileUpload>
<fileRename>truefileRename>
<fileDelete>truefileDelete>
accessControl>
你可以配置为,:
<accessControl>
<role>adminrole>
<resourceType>*resourceType>
<folder>/folder>
<folderView>truefolderView>
<folderCreate>truefolderCreate>
<folderRename>truefolderRename>
<folderDelete>truefolderDelete>
<fileView>truefileView>
<fileUpload>truefileUpload>
<fileRename>truefileRename>
<fileDelete>truefileDelete>
accessControl>
<accessControl>
<role>commonrole>
<resourceType>*resourceType>
<folder>/folder>
<folderView>truefolderView>
<folderCreate>falsefolderCreate>
<folderRename>falsefolderRename>
<folderDelete>falsefolderDelete>
<fileView>truefileView>
<fileUpload>truefileUpload>
<fileRename>truefileRename>
<fileDelete>truefileDelete>
accessControl>
只要在session中配置用户分类即可,变量key为这个(CKFinder_UserRole),当然你可以修改下面这个
<userRoleSessionVar>CKFinder_UserRoleuserRoleSessionVar>
工欲善其事必先利其器,所以我们先来分析一下:
上传url:
参数:
看到这里我们会发现,ckfinder的上传参数中根本没有包含文件名称,只包含文件路径和ckfinder自定义的命令观察通过观察源码得知,ckfinder自定义了好多关于文件的命令,具体的大家可以自行探索,这里由于篇幅原因不再赘述
但是我们通过观察返回值,返现response返回一系列字符串,返回文件名称,
到了这里我们就可以想到怎么破解了,
只要我们能得到返回response返回的内容不就好了
说干就干:
通过观察源码,我们发现ckfinder所有的请求都会交给com.ckfinder.connector.ConnectorServlet,这个类来处理,所以我们只要继承这个类,并覆写里面的方法就能是该功能,这只是一个servlet,具体的我们只要复写servlet的doget和dopost方法即可,具体来时只是dopost方法,因为文件上传都是post方法
这个并不是难点,难点是,我们如何获取response返回的内容尼?
这时我想到了动态代理的方法,但是这里只是一个简单的功能实现并不需要折磨做,静态代理即可,所以我们只需要实现自己的response实现类,然后在自己的ckfinder处理类中替换response即可
当然大家如果有更简单的方法,欢迎留言告知!!,这个方法的好处就是不用修改源码
想到了这个,我们就可以开始写代码,
但是由于httpservletresponse的方法过多,所以我们可以自己先写一个适配器,然后自己再去基层这个适配器,然后再实现httpservletresponse接口:
httpservletresponse适配器代码如下:
package com.office.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Locale;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
/**
* @author 都市桃源 (只要路对,就不怕路远)
* @since 2016年11月2日
* @!TODO response 适配器类
* 项目名称:MyOffice
*/
public class ResponseAdapter implements HttpServletResponse{
private HttpServletResponse response ;
public ResponseAdapter(HttpServletResponse response) {
super();
this.response = response;
}
@Override
public void flushBuffer() throws IOException {
response.flushBuffer();
}
@Override
public int getBufferSize() {
return response.getBufferSize();
}
@Override
public String getCharacterEncoding() {
return response.getCharacterEncoding();
}
@Override
public String getContentType() {
return response.getContentType();
}
@Override
public Locale getLocale() {
return response.getLocale();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {//这个是我们唯一要覆写的方法
return response.getOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
return response.getWriter();
}
@Override
public boolean isCommitted() {
return response.isCommitted();
}
@Override
public void reset() {
response.reset();
}
@Override
public void resetBuffer() {
response.resetBuffer();
}
@Override
public void setBufferSize(int size) {
response.setBufferSize(size);
}
@Override
public void setCharacterEncoding(String en) {
response.setCharacterEncoding(en);
}
@Override
public void setContentLength(int arg0) {
response.setContentLength(arg0);
}
@Override
public void setContentType(String arg0) {
response.setContentType(arg0);
}
@Override
public void setLocale(Locale arg0) {
response.setLocale(arg0);
}
@Override
public void addCookie(Cookie arg0) {
response.addCookie(arg0);
}
@Override
public void addDateHeader(String arg0, long arg1) {
response.addDateHeader(arg0, arg1);
}
@Override
public void addHeader(String arg0, String arg1) {
response.addHeader(arg0, arg1);
}
@Override
public void addIntHeader(String arg0, int arg1) {
response.addIntHeader(arg0, arg1);
}
@Override
public boolean containsHeader(String arg0) {
return response.containsHeader(arg0);
}
@Override
public String encodeRedirectURL(String arg0) {
return response.encodeRedirectURL(arg0);
}
@Override
public String encodeRedirectUrl(String arg0) {
return response.encodeRedirectUrl(arg0);
}
@Override
public String encodeURL(String arg0) {
return response.encodeURL(arg0);
}
@Override
public String encodeUrl(String arg0) {
return response.encodeUrl(arg0);
}
@Override
public String getHeader(String arg0) {
return response.getHeader(arg0);
}
@Override
public Collection getHeaderNames() {
return response.getHeaderNames();
}
@Override
public Collection getHeaders(String arg0) {
return response.getHeaders(arg0);
}
@Override
public int getStatus() {
return response.getStatus();
}
@Override
public void sendError(int arg0) throws IOException {
response.sendError(arg0);
}
@Override
public void sendError(int arg0, String arg1) throws IOException {
response.sendError(arg0, arg1);
}
@Override
public void sendRedirect(String arg0) throws IOException {
response.sendRedirect(arg0);
}
@Override
public void setDateHeader(String arg0, long arg1) {
response.setDateHeader(arg0, arg1);
}
@Override
public void setHeader(String arg0, String arg1) {
response.setHeader(arg0, arg1);
}
@Override
public void setIntHeader(String arg0, int arg1) {
response.setIntHeader(arg0, arg1);
}
@Override
public void setStatus(int arg0) {
response.setStatus(arg0);
}
@Override
public void setStatus(int arg0, String arg1) {
response.setStatus(arg0,arg1);
}
}
其实这类写不写都没关系,但是如果我们以后要做更多的扩展,就不必每次都覆写httpservletresponse这个接口了
通过
静态代理类
package com.office.util;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
/**
* @author 都市桃源 (只要路对,就不怕路远)
* @since 2016年11月2日
* @!TODO 自定义httpservletResponse,实现所有方法
* 项目名称:MyOffice
*/
public class HttpServletResponseProxy extends ResponseAdapter implements HttpServletResponse{
private OutputStreamProxy pwxy;
//自己的httpservletOutputStream类
public HttpServletResponseProxy(HttpServletResponse response) {
super(response);
}
public OutputStreamProxy getPwxy() {
return pwxy;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
pwxy=new OutputStreamProxy(super.getOutputStream());
return pwxy;
}
}
**追踪源码发现ckfindersevlet
向页面输出内容使用的是outputStream形式,而不是Wrtter方式,所以我们只要复写getOutputStem**
方法即可,返回我们自己实现的httpservletOutputStream实现类
具体代码
package com.office.util;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
public class OutputStreamProxy extends ServletOutputStream{
private ServletOutputStream out;
public OutputStreamProxy(ServletOutputStream out) {
super();
this.out = out;
}
@Override
public void write(int b) throws IOException {
ResponseContentUtil.getBuf().append((char)b);
//将response输出的内容保存在response缓存区中
out.write(b);
}
}
缓冲区实现编写:
package com.office.util;
/**
* @author 都市桃源 (只要路对,就不怕路远)
* @since 2016年11月2日
* @!TODO 获取response 内容
* 项目名称:MyOffice
*/
public class ResponseContentUtil {
static ThreadLocal bufs=new ThreadLocal();
public static StringBuffer getBuf(){
StringBuffer buf=bufs.get();
if(buf==null){
buf= new StringBuffer();
bufs.set(buf);
}
return buf;
}
public static void clear(){//清空缓存
bufs.set(new StringBuffer(""));
}
}
**重点:这里必须使用threadlocal实现,
因为我们必须保证在整个请求中操作的都是个缓存区**
package com.office.filter;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.ckfinder.connector.ConnectorServlet;
import com.office.biz.FileInfoBiz;
import com.office.entity.Fileinfo;
import com.office.util.HttpServletResponseProxy;
import com.office.util.ResponseContentUtil;
public class MyConnectorServlet extends ConnectorServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//获取上传命令
String command=request.getParameter("command");
if(command==null){
super.doPost(request, response);
return;
}
if(command.equals("FileUpload")){
//每次上传前清空缓存
ResponseContentUtil.clear();
HttpServletResponse myResponse=new HttpServletResponseProxy(response);
super.doPost(request, myResponse);
StringBuffer buf=ResponseContentUtil.getBuf();//获取response返回内容
//获取文件名
String fileName=new String(buf.substring(0,buf.indexOf("|")));
//获取当前目录
String currentFolder=request.getParameter("currentFolder");
Fileinfo info=new Fileinfo();
info.setFilename(fileName);
info.setRemark("office第二组办公系统文件");
info.setFilepath(currentFolder+fileName);
Timestamp time=new Timestamp(System.currentTimeMillis());
info.setCreatedate(time);
info.setFiletypeinfo(null);
ServletContext sc = request.getSession().getServletContext();
//获取spring容器
ApplicationContext app=WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
FileInfoBiz infobiz=app.getBean(FileInfoBiz.class);
//添加到数据库
infobiz.add(info);
System.out.println("上传文件路径---------"+currentFolder+fileName);
}else if(command.equals("DeleteFiles")){
List files=new ArrayList();
super.doPost(request, response);
int i=0;
String paramName = "files[" + i + "][name]";
while (request.getParameter(paramName) != null) {
//封装文件属性
String user=request.getParameter("user");
String infoName=request.getParameter(paramName);
String type=request.getParameter("files[" + i + "][type]");
String folder=request.getParameter("files[" + i + "][folder]");
//封装实体
Fileinfo info=new Fileinfo();
info.setFilename(infoName);
Timestamp time=new Timestamp(System.currentTimeMillis());
info.setCreatedate(time);
info.setFileowner(user);
info.setFilepath(folder+infoName);
info.setRemark("office第二组办公系统文件");
info.setFiletypeinfo(null);
files.add(info);
paramName = "files[" + (++i) + "][name]";
System.out.println("删除路径"+folder+infoName);
}
ServletContext sc = request.getSession().getServletContext();
//获取spring容器
ApplicationContext app=WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
FileInfoBiz infobiz=app.getBean(FileInfoBiz.class);
//根据属性批量删除文件
try {
infobiz.deleteBatchByProperty(files, "filename");
} catch (Exception e) {
System.out.println("删除异常");
}
}else if(command.equals("RenameFile")){
super.doPost(request, response);
String fileName=request.getParameter("fileName");
String newFileName=request.getParameter("newFileName");
ServletContext sc = request.getSession().getServletContext();
//获取spring容器
ApplicationContext app=WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
FileInfoBiz infobiz=app.getBean(FileInfoBiz.class);
infobiz.updateFileName(fileName,newFileName);
}else{
super.doPost(request, response);
}
}
}
文件重命名,移动,删除分析与文件上传一致,所以
大家可以
分析分析,也是很简单。
注意1:
为了使struts2的过滤器和ckfinder的servlet过滤路径
不冲突:两种解决方案:
方案1:
修改struts2的过滤路径为.action或者.do
方案二:
自己覆写struts2过滤的过滤方法,不过滤ckfinder的路径:
package com.office.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter;
public class MyStrutsFilter extends StrutsPrepareAndExecuteFilter{
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request=(HttpServletRequest) req;
String url=request.getRequestURI();
if(url==null){
super.doFilter(req, resp, chain);
return;
}
if(url.indexOf("/ckfinder/core/connector/")>=0){//放行次路径
chain.doFilter(req, resp);
}else{
super.doFilter(req, resp, chain);
}
}
@Override
protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
// TODO Auto-generated method stub
super.postInit(dispatcher, filterConfig);
}
}
我没有贴源码的习惯,如果真的看不懂可以留言问我要,不过大部分问题都提到了,如果还有没提到的,还望见谅
对了破解的ckfinder.js我会上传的csdn上欢迎大家下载
地址: