SpringMVC整合KindEditor,实现图片上传、预览、删除

之前我写过一篇关于《DWZ(JUI)整合Kindeditor应用于dialog弹出框》,只是讨论了前端的应用,并没有详细介绍后台代码实现。本文除了介绍后台的代码实现,还顺便介绍一些KindEditor的前端应用技巧。

以前的是SSH框架实现KindEditor插件的上传、图片空间管理功能。现在的项目底层框架换成了springmvc,代码虽然变化不大,但是需要注意一些细节的实现。
具体后台实现:

@Controller
@RequestMapping("/kindeditor")
public class KindEditorController extends  BaseController {
     
    private Logger logger = LoggerFactory.getLogger(KindEditorController.class);

    /**
    *文件上传功能
    */
    @ResponseBody
    @RequestMapping(value ="/fileUpload")
    public Map fileUpload(HttpServletRequest request,
                                          HttpServletResponse response) throws ServletException, IOException,
            FileUploadException {
        //这里的路径可以是绝对路径,也可以获取项目下的目录,这里使用的是绝对路径;  
        //String savePath = request.getServletContext().getRealPath("/")+"yourPath/" ;
        String savePath = "yourSavePath"+"/" ;
        //这里的路径需要符合下面另一个RequestMapping方法
        String saveUrl = request.getContextPath() + "/kindeditor/";
        // 定义允许上传的文件扩展名
        HashMap extMap = new HashMap();
        extMap.put("image", "gif,jpg,jpeg,png,bmp");
        extMap.put("flash", "swf,flv");
        extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
        extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2");

        // 最大文件大小
        long maxSize = 1000000;

        response.setContentType("text/html; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        if (!ServletFileUpload.isMultipartContent(request)) {
            return getError("请选择文件。");
        }
        String dirName = request.getParameter("dir");
        if (dirName == null) {
            dirName = "image";
        }
        if (!extMap.containsKey(dirName)) {
            return getError("目录名不正确。");
        }
        // 创建文件夹
        savePath += dirName + "/";
        saveUrl += dirName + "/";
        File saveDirFile = new File(savePath);
        if (!saveDirFile.exists()) {
            saveDirFile.mkdirs();
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String ymd = sdf.format(new Date());
        savePath += ymd + "/";
        saveUrl += ymd + "/";
        File dirFile = new File(savePath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        //这里需要后台springmvc-servlet.xml配置multipartResolver
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        multipartRequest.setCharacterEncoding("UTF-8");
        MultipartFile qqfile =multipartRequest.getFiles("imgFile").get(0);
        String oldName=qqfile.getOriginalFilename();
        String ext=qqfile.getOriginalFilename().substring(oldName.lastIndexOf(".") + 1);
        if (!Arrays.asList(extMap.get(dirName).split(",")).contains(ext)) {

            return getError("非常抱歉,目前上传附件格式类型只允许为:
"
+ extMap.get(dirName) + ",你选择的文件【" + oldName + "】不符合要求,无法上传!"); } String fileSizeNumber = fileMaxSize.substring(0, fileMaxSize.indexOf("M")); if (qqfile.getSize() > Long.valueOf(fileSizeNumber) * 1000 * 1000) { return getError("您选择的文件【" + oldName+ "】大小超过" + fileMaxSize + "限制,无法上传!"); } String fileName = null; ScmUploadFile uploadFile =null; String size=this.calculateFileSize(qqfile.getSize()); try { String createTime= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); fileName = this.upload(qqfile, savePath); uploadFile = new ScmUploadFile(); uploadFile.setName(oldName); uploadFile.setPath(savePath+fileName); uploadFile.setSize(size); uploadFile.setCreateTime(createTime); uploadFile.setExt(ext); service.save(uploadFile); Map succMap = new HashMap(); succMap.put("error", 0); succMap.put("url", saveUrl + fileName); return succMap; } catch (Exception e) { e.printStackTrace(); logger.error(e.getMessage()); return getError("您选择的文件【" + oldName+ "】上传失败!原因是:" + e.getMessage() + ""); } } private Map getError(String message) { Map msg = new HashMap(); msg.put("error", 1); msg.put("message", message); return msg; } /** *图片/文件空间管理功能 */ @ResponseBody @RequestMapping(value ="/fileManage") public Object fileManager(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 根目录路径,可以指定绝对路径,比如 /var/www/ String rootPath = "yourSavePath"+"/"; // 根目录URL,可以指定绝对路径,比如 http://www.yoursite.com/ String rootUrl = request.getContextPath() + "/kindeditor/"; // 图片扩展名 String[] fileTypes = new String[] { "gif", "jpg", "jpeg", "png", "bmp" }; String dirName = request.getParameter("dir"); if (dirName != null) { if(!Arrays.asList(new String[]{ "image", "flash", "media", "file"}).contains(dirName)){ return "Invalid Directory name."; } rootPath += dirName + "/"; rootUrl += dirName + "/"; File saveDirFile = new File(rootPath); if (!saveDirFile.exists()) { saveDirFile.mkdirs(); } } //根据path参数,设置各路径和URL String path = request.getParameter("path") != null ? request.getParameter("path") : ""; String currentPath = rootPath + path; String currentUrl = rootUrl + path; String currentDirPath = path; String moveupDirPath = ""; if (!"".equals(path)) { String str = currentDirPath.substring(0, currentDirPath.length() - 1); moveupDirPath = str.lastIndexOf("/") >= 0 ? str.substring(0, str.lastIndexOf("/") + 1) : ""; } //排序形式,name or size or type String order = request.getParameter("order") != null ? request.getParameter("order").toLowerCase() : "name"; //不允许使用..移动到上一级目录 if (path.indexOf("..") >= 0) { return "Access is not allowed."; } //最后一个字符不是/ if (!"".equals(path) && !path.endsWith("/")) { return "Parameter is not valid."; } //目录不存在或不是目录 File currentPathFile = new File(currentPath); if(!currentPathFile.isDirectory()){ return "Directory does not exist."; } //遍历目录取的文件信息 List> fileList = new ArrayList>(); if(currentPathFile.listFiles() != null) { for (File file : currentPathFile.listFiles()) { Hashtable hash = new Hashtable(); String fileName = file.getName(); if(file.isDirectory()) { hash.put("is_dir", true); hash.put("has_file", (file.listFiles() != null)); hash.put("filesize", 0L); hash.put("is_photo", false); hash.put("filetype", ""); } else if(file.isFile()){ String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); hash.put("is_dir", false); hash.put("has_file", false); hash.put("filesize", file.length()); hash.put("is_photo", Arrays.asList(fileTypes).contains(fileExt)); hash.put("filetype", fileExt); } hash.put("filename", fileName); hash.put("datetime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified())); fileList.add(hash); } } if ("size".equals(order)) { Collections.sort(fileList, new SizeComparator()); } else if ("type".equals(order)) { Collections.sort(fileList, new TypeComparator()); } else { Collections.sort(fileList, new NameComparator()); } Map result = new HashMap(); result.put("moveup_dir_path", moveupDirPath); result.put("current_dir_path", currentDirPath); result.put("current_url", currentUrl); result.put("total_count", fileList.size()); result.put("file_list", fileList); return result; } class NameComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable) a; Hashtable hashB = (Hashtable) b; if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { return -1; } else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { return 1; } else { return ((String) hashA.get("filename")) .compareTo((String) hashB.get("filename")); } } } class SizeComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable) a; Hashtable hashB = (Hashtable) b; if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { return -1; } else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { return 1; } else { if (((Long) hashA.get("filesize")) > ((Long) hashB .get("filesize"))) { return 1; } else if (((Long) hashA.get("filesize")) < ((Long) hashB .get("filesize"))) { return -1; } else { return 0; } } } } class TypeComparator implements Comparator { public int compare(Object a, Object b) { Hashtable hashA = (Hashtable) a; Hashtable hashB = (Hashtable) b; if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) { return -1; } else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) { return 1; } else { return ((String) hashA.get("filetype")) .compareTo((String) hashB.get("filetype")); } } } /** * 处理文件上传 * * @param file * @param absPath 存放文件的目录的绝对路径 * @return */ public String upload(MultipartFile file, String absPath) { long fileTimeStamp = new Date().getTime() + new Random().nextInt(100000); String fileName = fileTimeStamp+"-"+file.getOriginalFilename(); try { File targetFile = new File(absPath, fileName); FileUtils.writeByteArrayToFile(targetFile, file.getBytes()); } catch (IOException e) { e.printStackTrace(); } return fileName; } public String calculateFileSize(long size){ //字节数少于1024,直接以B为单位 if (size < 1024) { return String.valueOf(size) + "B"; } else { size = size / 1024; } //字节数除于1024之后,少于1024,则可直接以KB作为单位 if (size < 1024) { return String.valueOf(size) + "KB"; } else { size = size / 1024; } if (size < 1024) { //以MB为单位的话,保留最后1位小数 size = size * 100; return String.valueOf((size / 100)) + "." + String.valueOf((size % 100)) + "MB"; } else { //以GB为单位 size = size * 100 / 1024; return String.valueOf((size / 100)) + "." + String.valueOf((size % 100)) + "GB"; } } /** * 删除文件 * @param fileAbsSavePath 要删除的图片的绝对保存路径 */ public static void deleteFile(String fileAbsSavePath) { File file=new File(fileAbsSavePath); if(file.exists()) file.delete(); } /** * 根据文件名获取ContentType * @param suffix 文件名后缀 * @return ContentType */ public static String getContentType(String suffix) { if(!StringUtils.isEmpty(suffix)){ //uploadProps是properties对象获取配置文件里的文件名后缀对应的ContentType, //contentType.proerties文件在本文末尾 Object contentType = uploadProps.get(suffix); if(contentType != null) return contentType.toString(); } return "text/html"; } @RequestMapping(value ="/{fileType}/{uploadDate}/{fileName}.{suffix}") public void attached(HttpServletRequest request, HttpServletResponse response, @PathVariable String fileType, @PathVariable String uploadDate, @PathVariable String suffix, @PathVariable String fileName) { //根据suffix设置响应ContentType response.setContentType(this.getContentType(suffix)); InputStream is = null; OutputStream os = null; try { File file = new File("yourSavePath"+"/" + fileType + "/" + uploadDate + "/" + fileName + "." + suffix); is = new FileInputStream(file); byte[] buffer = new byte[is.available()]; is.read(buffer); os = new BufferedOutputStream(response.getOutputStream()); os.write(buffer); os.flush(); } catch (Exception e) { //判断suffix //图片请求可以在此显示一个默认图片 //file显示文件已损坏等错误提示... logger.error("读取文件失败", e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { logger.error("读取文件失败", e); } if (os != null) { try { os.close(); } catch (IOException e) { logger.error("读取文件失败", e); } } } } } /** *图片空间删除功能 */ @ResponseBody @RequestMapping("deleteImg") public Map deleteImg(String url) { Map map = new HashMap(); map.put("res", 0); if(!StringUtils.isEmpty(url)) { String absPath = "yourSavePath" + url.replace("/kindeditor",""); UploadFileUtil.deleteFile(absPath); service.deleteByPath(absPath); map.put("res", 1); } return map; } }

前端实现:
为了结合后台图片删除功能需要,修改kindeditor\plugins\filemanager\filemanager.js的200-209行代码

$(".xl_span").click(function(){
     //这里的.xl_span对应着刚才插入的删除按钮上的class
                var $this=K(this);
                if(!confirm('确定删除吗 ?请慎重操作,删除之后之前引用的地方会找不到图片')) {
    //提示,如果点击取消则直接退出
                    return false;
                }
                $.post('/kindeditor/deleteImg',{url:$this.attr("data-url")},function(data){
     //jquery的post,action为指定的配合后端用,url是获取刚才存在删除按钮上的图片路径,你完全可以用别的来写,因为用不好kindeditor的js库才用的jquery
                    data.res==1?$this.parent().remove():alert("删除出现错误");//如果返回1则直接删除 图片,名字的Div达到即时删除,否则提示
                    if(K(".ke-plugin-filemanager-body").children().length<1){K(".ke-plugin-filemanager-body").html("没有图片了")}//检查是否没有图片了
                });
            });

前端页面使用配置

  var DK = {}
    DK.kindeditor={basePath:'include/kindeditor/',upload:'/kindeditor/fileUpload',filemanager:'/kindeditor/fileManage',myForm:'yourform',cssPath:'include/kindeditor/plugins/code/prettify.css'};

前端部分参照《DWZ(JUI)整合Kindeditor应用于dialog弹出框》,这里需要修改一个地方,把步骤1里的20行代码的editor定义为全局变量 ,即在js文件里第一行附近定义var editor,然后文章里的步骤1第20行去掉var,因为最外面已经定义了。这样就可以在其他使用Kindeditor的地方使用js赋值到富文本里,即editor.html(“你赋值的”);还可以实现设置富文本只读,即editor.readonly(true);

contentType.proerties配置文件

#contentType.proerties
ez=application/andrew-inset
hqx=application/mac-binhex40
cpt=application/mac-compactpro
doc=application/msword
bin=application/octet-stream
dms=application/octet-stream
lha=application/octet-stream
lzh=application/octet-stream
exe=application/octet-stream
class=application/octet-stream
so=application/octet-stream
dll=application/octet-stream
oda=application/oda
pdf=application/pdf
ai=application/postscript
eps=application/postscript
ps=application/postscript
smi=application/smil
smil=application/smil
mif=application/vnd.mif
xls=application/vnd.ms-excel
ppt=application/vnd.ms-powerpoint
wbxml=application/vnd.wap.wbxml
wmlc=application/vnd.wap.wmlc
wmlsc=application/vnd.wap.wmlscriptc
bcpio=application/x-bcpio
vcd=application/x-cdlink
pgn=application/x-chess-pgn
cpio=application/x-cpio
csh=application/x-csh
dcr=application/x-director
dir=application/x-director
dxr=application/x-director
dvi=application/x-dvi
spl=application/x-futuresplash
gtar=application/x-gtar
hdf=application/x-hdf
js=application/x-javascript
skp=application/x-koan
skd=application/x-koan
skt=application/x-koan
skm=application/x-koan
latex=application/x-latex
nc=application/x-netcdf
cdf=application/x-netcdf
sh=application/x-sh
shar=application/x-shar
swf=application/x-shockwave-flash
sit=application/x-stuffit
sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc
tar=application/x-tar
tcl=application/x-tcl
tex=application/x-tex
texinfo=application/x-texinfo
texi=application/x-texinfo
t=application/x-troff
tr=application/x-troff
roff=application/x-troff
man=application/x-troff-man
me=application/x-troff-me
ms=application/x-troff-ms
ustar=application/x-ustar
src=application/x-wais-source
xhtml=application/xhtml+xml
xht=application/xhtml+xml
zip=application/zip
au=audio/basic
snd=audio/basic
mid=audio/midi
midi=audio/midi
kar=audio/midi
mpga=audio/mpeg
mp2=audio/mpeg
mp3=audio/mpeg
aif=audio/x-aiff
aiff=audio/x-aiff
aifc=audio/x-aiff
m3u=audio/x-mpegurl
ram=audio/x-pn-realaudio
rm=audio/x-pn-realaudio
rpm=audio/x-pn-realaudio-plugin
ra=audio/x-realaudio
wav=audio/x-wav
pdb=chemical/x-pdb
xyz=chemical/x-xyz
bmp=image/bmp
gif=image/gif
ief=image/ief
jpeg=image/jpeg
jpg=image/jpeg
jpe=image/jpeg
png=image/png
tiff=image/tiff
tif=image/tiff
djvu=image/vnd.djvu
djv=image/vnd.djvu
wbmp=image/vnd.wap.wbmp
ras=image/x-cmu-raster
pnm=image/x-portable-anymap
pbm=image/x-portable-bitmap
pgm=image/x-portable-graymap
ppm=image/x-portable-pixmap
rgb=image/x-rgb
xbm=image/x-xbitmap
xpm=image/x-xpixmap
xwd=image/x-xwindowdump
igs=model/iges
iges=model/iges
msh=model/mesh
mesh=model/mesh
silo=model/mesh
wrl=model/vrml
vrml=model/vrml
css=text/css
html=text/html
htm=text/html
asc=text/plain
txt=text/plain
rtx=text/richtext
rtf=text/rtf
sgml=text/sgml
sgm=text/sgml
tsv=text/tab-separated-values
wml=text/vnd.wap.wml
wmls=text/vnd.wap.wmlscript
etx=text/x-setext
xsl=text/xml
xml=text/xml
mpeg=video/mpeg
mpg=video/mpeg
mpe=video/mpeg
qt=video/quicktime
mov=video/quicktime
mxu=video/vnd.mpegurl
avi=video/x-msvideo
movie=video/x-sgi-movie
ice=x-conference/x-cooltalk

你可能感兴趣的:(开发技巧,spring,mvc,kindeditor,前端,框架,应用)