Ueditor之前后端源碼的學(xué)習(xí)和簡(jiǎn)單的研究

開心一笑

剛來北京,租了一個(gè)小房,一樓,上淘寶買衣服,選了付錢了聯(lián)系賣家:“我已付款,請(qǐng)發(fā)貨。
”誰知那貨直接說:“我看到你地址了,自己上樓來拿吧!我就在你樓上?!?br> 拿你妹,老子付了郵費(fèi)的。。。送下來。

提出問題

Ueditor前后端源碼的學(xué)習(xí)和簡(jiǎn)單的研究???

解決問題

前提:

  • 假如你已經(jīng)看了我的前一篇文章,這一點(diǎn)很重要的啊,當(dāng)然不看也可以,因?yàn)槟阋呀?jīng)是一個(gè)高手,就像我一樣,哈哈;
  • 假如你已經(jīng)安裝tomcat服務(wù)器;
  • 假如你已經(jīng)把項(xiàng)目運(yùn)行到Eclipse上;
  • 假如你已經(jīng)有java基礎(chǔ);
  • 假如你對(duì)js有一定基礎(chǔ);
  • 假如你已經(jīng)下載ueditor1_4_3_3-src源碼,記得是開發(fā)的哦;

那么開始吧!

這是我的項(xiàng)目目錄

這里寫圖片描述

1.從訪問路徑http://localhost:8081/Test/_examples/simpleDemo.html,
我們主要是要看看,富文本框被加載出來之前,會(huì)調(diào)用哪些代碼,

不賣關(guān)子,很明顯,會(huì)調(diào)用后端的controller.jsp代碼,因?yàn)槲覀円呀?jīng)在ueditor.config配置了:

// 服務(wù)器統(tǒng)一請(qǐng)求接口路徑
, serverUrl: URL + "jsp/controller.js

看看controller.jsp代碼,上一篇文章我們已經(jīng)講了,要把這些代碼看作是后端代碼,很重要很重要的:

<%@ page language="java" contentType="text/html; charset=UTF-8"
import="com.baidu.ueditor.ActionEnter"
pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="true" %>
<%

    request.setCharacterEncoding( "utf-8" );
    response.setHeader("Content-Type" , "text/html");
    /** 項(xiàng)目根路徑 **/
    String rootPath = application.getRealPath( "/" );
    /** 調(diào)用后端的ActionEnter類,并執(zhí)行exec方法 **/
    out.write( new ActionEnter( request, rootPath ).exec() );
    
%>

我們就到ActionEnter.java看看吧,這個(gè)類就是前端調(diào)用后端的唯一入口,也只有這個(gè)入口了,
記住第一章有講了,要把源碼復(fù)制到src下,進(jìn)行調(diào)試哦!不知道先看第一章吧?。。。。?/p>

package com.baidu.ueditor;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.baidu.ueditor.define.ActionMap;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.State;
import com.baidu.ueditor.hunter.FileManager;
import com.baidu.ueditor.hunter.ImageHunter;
import com.baidu.ueditor.upload.Uploader;

public class ActionEnter {

private HttpServletRequest request = null;

private String rootPath = null;
private String contextPath = null;

private String actionType = null;

private ConfigManager configManager = null;
/** action統(tǒng)一入口 **/
public ActionEnter ( HttpServletRequest request, String rootPath ) {
    
    this.request = request;
    /** rootPath = /Test/ **/
    this.rootPath = rootPath;
    /** actionType = config **/
    this.actionType = request.getParameter( "action" );
    /** contextPath = /Test **/
    this.contextPath = request.getContextPath();
    /** 調(diào)用ConfigManager **/
    this.configManager = ConfigManager.getInstance( this.rootPath, this.contextPath, request.getRequestURI() );
    
}

2.ConfigManager類主要用來讀取后端的配置文件,就是config.json這個(gè)文件,事實(shí)上這個(gè)文件應(yīng)該放在后端的。

/**
 * 配置管理器
 * @author hancong03@baidu.com
 *
 */
public final class ConfigManager {

private final String rootPath;
private final String originalPath;
private final String contextPath;
/** 存放備注文件 **/
private static final String configFileName = "config.json";
private String parentPath = null;
private JSONObject jsonConfig = null;
// 涂鴉上傳filename定義
private final static String SCRAWL_FILE_NAME = "scrawl";
// 遠(yuǎn)程圖片抓取filename定義
private final static String REMOTE_FILE_NAME = "remote";

/*
 * 通過一個(gè)給定的路徑構(gòu)建一個(gè)配置管理器, 該管理器要求地址路徑所在目錄下必須存在config.properties文件
 */
private ConfigManager ( String rootPath, String contextPath, String uri ) throws FileNotFoundException, IOException {
    
    rootPath = rootPath.replace( "\\", "/" );
    //下面的rootPath就是我的根路徑
    // rootPath=D:/workspace_de_client/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/Test/
    this.rootPath = rootPath;
    this.contextPath = contextPath;
    //請(qǐng)求路徑 url = /Test/jsp/controller.jsp
    if ( contextPath.length() > 0 ) {
        // D:/workspace_de_client/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/Test//jsp/controller.jsp
        this.originalPath = this.rootPath + uri.substring( contextPath.length() );
    } else {
        this.originalPath = this.rootPath + uri;
    }
    /** 調(diào)用當(dāng)前類的初始化環(huán)境方法 initEnv **/
    this.initEnv();
    
}

//上面的方法無非就是獲得controller.jsp這個(gè)類所在的真實(shí)目錄而已


//下面看看initEnv()這個(gè)方法

private void initEnv () throws FileNotFoundException, IOException {
    /**  **/
    File file = new File( this.originalPath );
    
    if ( !file.isAbsolute() ) {
        file = new File( file.getAbsolutePath() );
    }
    /** 獲得文件的父路徑,也就是  ..../jsp **/
    this.parentPath = file.getParent();
    /** 讀取配置文件,這個(gè)方法比較重要,往下看 **/
    String configContent = this.readFile( this.getConfigPath() );
    
    try{
        /** 把返回的的json字符串扔進(jìn)JsonObject對(duì)象中 **/
        JSONObject jsonConfig = new JSONObject( configContent );
        this.jsonConfig = jsonConfig;
    } catch ( Exception e ) {
        this.jsonConfig = null;
    }
    
}

/** 獲得配置路徑,記住config.json是和controller.jsp放在同一個(gè)目錄下的,很坑有木有 **/
private String getConfigPath () {
    /** 拼湊config.json的真實(shí)路徑 **/
    return this.parentPath + File.separator + ConfigManager.configFileName;
}

private String[] getArray ( String key ) {
    
    JSONArray jsonArray = this.jsonConfig.getJSONArray( key );
    String[] result = new String[ jsonArray.length() ];
    
    for ( int i = 0, len = jsonArray.length(); i < len; i++ ) {
        result[i] = jsonArray.getString( i );
    }
    
    return result;
    
}
/** 獲得配置文件的內(nèi)容,變成字符串返回 **/
private String readFile ( String path ) throws IOException {
    
    StringBuilder builder = new StringBuilder();
    
    try {
        
        InputStreamReader reader = new InputStreamReader( new FileInputStream( path ), "UTF-8" );
        BufferedReader bfReader = new BufferedReader( reader );
        
        String tmpContent = null;
        
        while ( ( tmpContent = bfReader.readLine() ) != null ) {
            builder.append( tmpContent );
        }
        
        bfReader.close();
        
    } catch ( UnsupportedEncodingException e ) {
        // 忽略
    }
    //過濾輸入字符串, 剔除多行注釋以及替換掉反斜杠
    return this.filter( builder.toString() );
    
}

// 過濾輸入字符串, 剔除多行注釋以及替換掉反斜杠
private String filter ( String input ) {
    
    return input.replaceAll( "/\\*[\\s\\S]*?\\*/", "" );
    
}

從上面的方法中,讀取配置文件的所有后端代碼就都執(zhí)行完了吧?。。?!很簡(jiǎn)單吧!?。。?/p>

3.后端代碼執(zhí)行完之后,富文本框就初始化出來了,很有成就感吧?。?br> 如圖:

這里寫圖片描述

我們點(diǎn)擊上傳圖片的按鈕,選擇一張圖片上傳,好,接下來就看看前端是如何調(diào)用,以及后端是如何保存文件的吧??!

我們看simpleupload.js這個(gè)文件,它是實(shí)現(xiàn)單文本上傳的主要前段代碼,很重要的

我們從53行開始看也就是:domUtils.on(input, 'change', function()
找不到可以Ctrl +F,相信這點(diǎn)技能還是有的吧,否則就不適合這個(gè)行業(yè)了 !?。?!

這里我要說明下,ueditor讀取配置文件的順序,是:

后端返回json配置文件 --> 
用戶自定義的配置文件 -->
ueditor.config(我不知道有沒有記錯(cuò),在家里寫,沒網(wǎng)?。。?!一切都是憑記憶啊)

/**
 * @description
 * 簡(jiǎn)單上傳:點(diǎn)擊按鈕,直接選擇文件上傳
 * @author Jinqn
 * @date 2014-03-31
 */
UE.plugin.register('simpleupload', function (){
    var me = this,
        isLoaded = false,
        containerBtn;
    /** 初始化上傳的圖片按鈕,人家名字取得好?。?! **/
    function initUploadBtn(){
        var w = containerBtn.offsetWidth || 20,
            h = containerBtn.offsetHeight || 20,
            btnIframe = document.createElement('iframe'),
            btnStyle = 'display:block;width:' + w + 'px;height:' + 
            h + 'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);
            -moz-opacity:0;-khtml-opacity: 0;
            opacity: 0;cursor:pointer;';

        domUtils.on(btnIframe, 'load', function(){

            var timestrap = (+new Date()).toString(36),
                wrapper,
                btnIframeDoc,
                btnIframeBody;

            btnIframeDoc = (btnIframe.contentDocument || btnIframe.contentWindow.document);
            btnIframeBody = btnIframeDoc.body;
            wrapper = btnIframeDoc.createElement('div');

            wrapper.innerHTML = '<form id="edui_form_' + timestrap + '" target="edui_iframe_'
            + timestrap + '" method="POST" enctype="multipart/form-data" action="' + me.getOpt('serverUrl') + '" ' +
            'style="' + btnStyle + '">' +
            '<input id="edui_input_' + timestrap + '" type="file" accept="image/*" name="' 
            + me.options.imageFieldName + '" ' +
            'style="' + btnStyle + '">' +
            '</form>' +
            '<iframe id="edui_iframe_' + timestrap + '" name="edui_iframe_' + timestrap 
            + '" style="display:none;width:0;height:0;border:0;margin:0;padding:0;position:absolute;"></iframe>';

            wrapper.className = 'edui-' + me.options.theme;
            wrapper.id = me.ui.id + '_iframeupload';
            btnIframeBody.style.cssText = btnStyle;
            btnIframeBody.style.width = w + 'px';
            btnIframeBody.style.height = h + 'px';
            btnIframeBody.appendChild(wrapper);

            if (btnIframeBody.parentNode) {
                btnIframeBody.parentNode.style.width = w + 'px';
                btnIframeBody.parentNode.style.height = w + 'px';
            }

            var form = btnIframeDoc.getElementById('edui_form_' + timestrap);
            var input = btnIframeDoc.getElementById('edui_input_' + timestrap);
            var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap);
            /** 點(diǎn)擊上傳圖片按鈕時(shí),調(diào)用的代碼 **/
            domUtils.on(input, 'change', function(){
                if(!input.value) return;
                var loadingId = 'loading_' + (+new Date()).toString(36);
                var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
                /** 獲得配置文件中的imageActionName值 **/
                var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
                //獲取允許的文件格式
                var allowFiles = me.getOpt('imageAllowFiles');

                me.focus();
                me.execCommand('inserthtml', '<img class="loadingclass" id="' 
                + loadingId + '" src="' + me.options.themePath + me.options.theme 
                +'/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >');
                //這個(gè)方法先不看,它是后端執(zhí)行完圖片上傳后回調(diào)函數(shù)
                function callback(){
                    try{
                        var link, json, loader,
                            body = (iframe.contentDocument || iframe.contentWindow.document).body,
                            result = body.innerText || body.textContent || '';
                        json = (new Function("return " + result))();
                        link = me.options.imageUrlPrefix + json.url;
                        if(json.state == 'SUCCESS' && json.url) {
                            loader = me.document.getElementById(loadingId);
                            loader.setAttribute('src', link);
                            loader.setAttribute('_src', link);
                            loader.setAttribute('title', json.title || '');
                            loader.setAttribute('alt', json.original || '');
                            loader.removeAttribute('id');
                            domUtils.removeClasses(loader, 'loadingclass');
                        } else {
                            showErrorLoader && showErrorLoader(json.state);
                        }
                    }catch(er){
                        showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
                    }
                    form.reset();
                    domUtils.un(iframe, 'load', callback);
                }
                function showErrorLoader(title){
                    if(loadingId) {
                        var loader = me.document.getElementById(loadingId);
                        loader && domUtils.remove(loader);
                        me.fireEvent('showmessage', {
                            'id': loadingId,
                            'content': title,
                            'type': 'error',
                            'timeout': 4000
                        });
                    }
                }

                /* 判斷后端配置是否沒有加載成功 */
                if (!me.getOpt('imageActionName')) {
                    errorHandler(me.getLang('autoupload.errorLoadConfig'));
                    return;
                }
                // 判斷文件格式是否錯(cuò)誤
                var filename = input.value,
                    fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
                if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
                    showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
                    return;
                }
                
                // -----這里要注意了,這就是調(diào)用后端接口的重要代碼 start ----
                domUtils.on(iframe, 'load', callback);
                //給form標(biāo)簽的action設(shè)置請(qǐng)求路徑,不要問我form標(biāo)簽在哪里,在上面的初始化按鈕就有了                   
                form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params);
                //很簡(jiǎn)單,把form代碼提交了                    
                form.submit();
                // -------end  ----------------------------------------
            });

            var stateTimer;
            me.addListener('selectionchange', function () {
                clearTimeout(stateTimer);
                stateTimer = setTimeout(function() {
                    var state = me.queryCommandState('simpleupload');
                    if (state == -1) {
                        input.disabled = 'disabled';
                    } else {
                        input.disabled = false;
                    }
                }, 400);
            });
            isLoaded = true;
        });

        btnIframe.style.cssText = btnStyle;
        containerBtn.appendChild(btnIframe);
    }

    return {
        bindEvents:{
            'ready': function() {
                //設(shè)置loading的樣式
                utils.cssRule('loading',
                    '.loadingclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loading.gif\') no-repeat center center transparent;
                    border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
                    '.loaderrorclass{display:inline-block;cursor:default;background: url(\''
                    + this.options.themePath
                    + this.options.theme +'/images/loaderror.png\') no-repeat center center transparent;
                    border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
                    '}',
                    this.document);
            },
            /* 初始化簡(jiǎn)單上傳按鈕 */
            'simpleuploadbtnready': function(type, container) {
                containerBtn = container;
                me.afterConfigReady(initUploadBtn);
            }
        },
        outputRule: function(root){
            utils.each(root.getNodesByTagName('img'),function(n){
                if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
                    n.parentNode.removeChild(n);
                }
            });
        },
        commands: {
            'simpleupload': {
                queryCommandState: function () {
                    return isLoaded ? 0:-1;
                }
            }
        }
    }
});

好了,開始來調(diào)用后端的代碼了,一樣是調(diào)用ActionEnter.java這個(gè)類
前端的請(qǐng)求路徑是

"http://localhost:8081/Test/jsp/controller.jsp?action=uploadimage"


/**
 * 處理不同類型的回調(diào)函數(shù)
 * @return
 */
public String invoke() {
    //自己添加上去的
    Map<String, Integer> mapping = ActionMap.mapping;
    
    if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
        return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString();
    }
    
    if ( this.configManager == null || !this.configManager.valid() ) {
        return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString();
    }
    
    State state = null;
    // 獲得actionType類型碼 ActionMap這個(gè)類我就不介紹了,自己可以看看,主要是封裝的一些常量
    int actionCode = ActionMap.getType( this.actionType );
    
    Map<String, Object> conf = null;
    
    switch ( actionCode ) {
        //如果配置文件,執(zhí)行下面的方法,這個(gè)就是我們開始講的后端讀取的配置文件執(zhí)行的邏輯
        case ActionMap.CONFIG:
            return this.configManager.getAllConfig().toString();
        //這里是我們這次重點(diǎn)講解的路徑,圖片上傳,視頻上傳都執(zhí)行這個(gè)路基   
        case ActionMap.UPLOAD_IMAGE:
        case ActionMap.UPLOAD_SCRAWL:
        case ActionMap.UPLOAD_VIDEO:
        case ActionMap.UPLOAD_FILE:
            //發(fā)現(xiàn)沒有,這里獲得配置文件,看到這里,你應(yīng)該先看getConfig這個(gè)方法,跟著思路走,不是是跟著調(diào)試代碼走
            conf = this.configManager.getConfig( actionCode );
            //這里就是執(zhí)行文件上傳的方法了,看完上面代碼才可以看這里呀,不要急
            state = new Uploader( request, conf ).doExec();
            break;
            
        case ActionMap.CATCH_IMAGE:
            conf = configManager.getConfig( actionCode );
            String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) );
            state = new ImageHunter( conf ).capture( list );
            break;
            
        case ActionMap.LIST_IMAGE:
        case ActionMap.LIST_FILE:
            conf = configManager.getConfig( actionCode );
            int start = this.getStartIndex();
            state = new FileManager( conf ).listFile( start );
            break;
            
    }
    
    return state.toJSONString();
    
}

看看configManager.getConfig這個(gè)類
//如何是獲得后端的所有配置,調(diào)用這個(gè)方法
public JSONObject getAllConfig () {

    return this.jsonConfig;
    
}
//獲得部分的配置
public Map<String, Object> getConfig ( int type ) {
    
    Map<String, Object> conf = new HashMap<String, Object>();
    String savePath = null;
    
    switch ( type ) {
        
        case ActionMap.UPLOAD_FILE:
            conf.put( "isBase64", "false" );
            conf.put( "maxSize", this.jsonConfig.getLong( "fileMaxSize" ) );
            conf.put( "allowFiles", this.getArray( "fileAllowFiles" ) );
            conf.put( "fieldName", this.jsonConfig.getString( "fileFieldName" ) );
            savePath = this.jsonConfig.getString( "filePathFormat" );
            break;
        //上傳圖片邏輯    
        case ActionMap.UPLOAD_IMAGE:
            conf.put( "isBase64", "false" );
            conf.put( "maxSize", this.jsonConfig.getLong( "imageMaxSize" ) );
            conf.put( "allowFiles", this.getArray( "imageAllowFiles" ) );
            //看看看,走在路上別瞎看,看這里很重要的
            //imageFieldName 圖片名稱
            conf.put( "fieldName", this.jsonConfig.getString( "imageFieldName" ) );
            //圖片保存路徑,有沒有發(fā)現(xiàn)在config.json配置的imagePathFormat返回前端變成savePath
            savePath = this.jsonConfig.getString( "imagePathFormat" );
            break;
            
        case ActionMap.UPLOAD_VIDEO:
            conf.put( "maxSize", this.jsonConfig.getLong( "videoMaxSize" ) );
            conf.put( "allowFiles", this.getArray( "videoAllowFiles" ) );
            conf.put( "fieldName", this.jsonConfig.getString( "videoFieldName" ) );
            savePath = this.jsonConfig.getString( "videoPathFormat" );
            break;
            
        case ActionMap.UPLOAD_SCRAWL:
            conf.put( "filename", ConfigManager.SCRAWL_FILE_NAME );
            conf.put( "maxSize", this.jsonConfig.getLong( "scrawlMaxSize" ) );
            conf.put( "fieldName", this.jsonConfig.getString( "scrawlFieldName" ) );
            conf.put( "isBase64", "true" );
            savePath = this.jsonConfig.getString( "scrawlPathFormat" );
            break;
            
        case ActionMap.CATCH_IMAGE:
            conf.put( "filename", ConfigManager.REMOTE_FILE_NAME );
            conf.put( "filter", this.getArray( "catcherLocalDomain" ) );
            conf.put( "maxSize", this.jsonConfig.getLong( "catcherMaxSize" ) );
            conf.put( "allowFiles", this.getArray( "catcherAllowFiles" ) );
            conf.put( "fieldName", this.jsonConfig.getString( "catcherFieldName" ) + "[]" );
            savePath = this.jsonConfig.getString( "catcherPathFormat" );
            break;
            
        case ActionMap.LIST_IMAGE:
            conf.put( "allowFiles", this.getArray( "imageManagerAllowFiles" ) );
            conf.put( "dir", this.jsonConfig.getString( "imageManagerListPath" ) );
            conf.put( "count", this.jsonConfig.getInt( "imageManagerListSize" ) );
            break;
            
        case ActionMap.LIST_FILE:
            conf.put( "allowFiles", this.getArray( "fileManagerAllowFiles" ) );
            conf.put( "dir", this.jsonConfig.getString( "fileManagerListPath" ) );
            conf.put( "count", this.jsonConfig.getInt( "fileManagerListSize" ) );
            break;
            
    }
    
    conf.put( "savePath", savePath );
    conf.put( "rootPath", this.rootPath );
    
    return conf;
    
}

接下了這個(gè)類Uploader.java

package com.baidu.ueditor.upload;

import com.baidu.ueditor.define.State;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;

public class Uploader {
    private HttpServletRequest request = null;
    private Map<String, Object> conf = null;

    public Uploader(HttpServletRequest request, Map<String, Object> conf) {
        this.request = request;
        this.conf = conf;
    }

    public final State doExec() {
        String filedName = (String) this.conf.get("fieldName");
        State state = null;
        //重點(diǎn)在這里
        if ("true".equals(this.conf.get("isBase64"))) {
            //重點(diǎn)看這里,好了,知道我們要干嘛了吧?。?!看Base64Uploader類的代碼
            state = Base64Uploader.save(this.request.getParameter(filedName),
                    this.conf);
        } else {
            state = BinaryUploader.save(this.request, this.conf);
        }

        return state;
    }
}

接下來看這個(gè)類的方法:BaseUploader.java,這里的save方法就是把文件保存到硬盤上

package com.baidu.ueditor.upload;

import com.baidu.ueditor.PathFormat;
import com.baidu.ueditor.define.AppInfo;
import com.baidu.ueditor.define.BaseState;
import com.baidu.ueditor.define.FileType;
import com.baidu.ueditor.define.State;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class BinaryUploader {

    public static final State save(HttpServletRequest request,
            Map<String, Object> conf) {
        FileItemStream fileStream = null;
        boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null;

        if (!ServletFileUpload.isMultipartContent(request)) {
            return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT);
        }
        //common-io包中類,用于文件上傳
        ServletFileUpload upload = new ServletFileUpload(
                new DiskFileItemFactory());

        if ( isAjaxUpload ) {
            upload.setHeaderEncoding( "UTF-8" );
        }

        try {//獲取文件條目
            FileItemIterator iterator = upload.getItemIterator(request);

            while (iterator.hasNext()) {
                fileStream = iterator.next();

                if (!fileStream.isFormField())
                    break;
                fileStream = null;
            }

            if (fileStream == null) {
                return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
            }
            //獲得保存路徑
            String savePath = (String) conf.get("savePath");
            //文件原始名稱                
            String originFileName = fileStream.getName();
            //文件后綴              
            String suffix = FileType.getSuffixByFilename(originFileName);
            //原文講原始名稱   
            originFileName = originFileName.substring(0,
                    originFileName.length() - suffix.length());
            savePath = savePath + suffix;

            long maxSize = ((Long) conf.get("maxSize")).longValue();

            if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
                return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE);
            }

            savePath = PathFormat.parse(savePath, originFileName);
            //文件保存的真實(shí)物理路徑   
            String physicalPath = (String) conf.get("rootPath") + savePath;

            InputStream is = fileStream.openStream();
            //這里就是把文件保存到硬盤上,具體怎么保存的可以自己跟過去看看
            //State這個(gè)類很重要,是一個(gè)接口,它是返回到前端的數(shù)據(jù)
            State storageState = StorageManager.saveFileByInputStream(is,
                    physicalPath, maxSize);
            is.close();

            if (storageState.isSuccess()) {
                storageState.putInfo("url", PathFormat.format(savePath));
                storageState.putInfo("type", suffix);
                storageState.putInfo("original", originFileName + suffix);
            }

            return storageState;
        } catch (FileUploadException e) {
            return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
        } catch (IOException e) {
        }
        return new BaseState(false, AppInfo.IO_ERROR);
    }

    private static boolean validType(String type, String[] allowTypes) {
        List<String> list = Arrays.asList(allowTypes);

        return list.contains(type);
    }
}

最后,我們?cè)倏匆粋€(gè)類就是State這個(gè)類,它是一個(gè)接口,我們主要是看它的實(shí)現(xiàn)類BaseState
這個(gè)類很重要,很重要,很重要,重要事情說3遍:::

介紹下吧:

這個(gè)類主要是返回前端的數(shù)據(jù),格式就是下面這個(gè)樣子,格式一定要對(duì),否則前端會(huì)出現(xiàn)問題

package com.baidu.ueditor.define;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.baidu.ueditor.Encoder;

public class BaseState implements State {
    //狀態(tài)碼
    private boolean state = false;
    private String info = null;
    //里面存保存好的文件路徑和
    private Map<String, String> infoMap = new HashMap<String, String>();
    
    public BaseState () {
        this.state = true;
    }
    
    public BaseState ( boolean state ) {
        this.setState( state );
    }
    
    public BaseState ( boolean state, String info ) {
        this.setState( state );
        this.info = info;
    }
    
    public BaseState ( boolean state, int infoCode ) {
        this.setState( state );
        this.info = AppInfo.getStateInfo( infoCode );
    }
    
    public boolean isSuccess () {
        return this.state;
    }
    
    public void setState ( boolean state ) {
        this.state = state;
    }
    
    public void setInfo ( String info ) {
        this.info = info;
    }
    
    public void setInfo ( int infoCode ) {
        this.info = AppInfo.getStateInfo( infoCode );
    }
    
    @Override
    public String toJSONString() {
        return this.toString();
    }
    /** 這里很重要的,也很簡(jiǎn)單,它把infoMap手工拼湊成json字符串返回回去 **/
    public String toString () {
        
        String key = null;
        String stateVal = this.isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this.info;
        
        StringBuilder builder = new StringBuilder();
        
        builder.append( "{\"state\": \"" + stateVal + "\"" );
        
        Iterator<String> iterator = this.infoMap.keySet().iterator();
        
        while ( iterator.hasNext() ) {
            
            key = iterator.next();
            
            builder.append( ",\"" + key + "\": \"" + this.infoMap.get(key) + "\"" );
            
        }
        
        builder.append( "}" );

        return Encoder.toUnicode( builder.toString() );

    }

    @Override
    public void putInfo(String name, String val) {
        this.infoMap.put(name, val);
    }

    @Override
    public void putInfo(String name, long val) {
        this.putInfo(name, val+"");
    }

}

4.上面后端的代碼已經(jīng)調(diào)完了,接下來就是后端數(shù)據(jù)返回到前端

之前講過,看前端的simpleupload.js

后端返回?cái)?shù)據(jù)后會(huì)調(diào)用回調(diào)函數(shù)的callback()方法

function callback(){
                try{
                    var link, json, loader,
                        body = (iframe.contentDocument || iframe.contentWindow.document).body,
                        result = body.innerText || body.textContent || '';
                    //這里result就是后端返回的數(shù)據(jù)
                    json = (new Function("return " + result))();
                    //imageUrlPrefix這個(gè)很重要很重要很重要,如果沒配置的話,圖片可能顯示不出來
                    //link就是圖片的路徑
                    link = me.options.imageUrlPrefix + json.url;
                    if(json.state == 'SUCCESS' && json.url) {
                        loader = me.document.getElementById(loadingId);
                        loader.setAttribute('src', link);
                        loader.setAttribute('_src', link);
                        loader.setAttribute('title', json.title || '');
                        loader.setAttribute('alt', json.original || '');
                        loader.removeAttribute('id');
                        domUtils.removeClasses(loader, 'loadingclass');
                    } else {
                        showErrorLoader && showErrorLoader(json.state);
                    }
                }catch(er){
                    showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'));
                }
                form.reset();
                domUtils.un(iframe, 'load', callback);
            }
            function showErrorLoader(title){
                if(loadingId) {
                    var loader = me.document.getElementById(loadingId);
                    loader && domUtils.remove(loader);
                    me.fireEvent('showmessage', {
                        'id': loadingId,
                        'content': title,
                        'type': 'error',
                        'timeout': 4000
                    });
                }
            }

5.結(jié)束了,好累啊,可是朋友們,這樣就滿足了嗎?NO NO NO,這里只是講源碼實(shí)現(xiàn),至于如何和真實(shí)項(xiàng)目結(jié)合才是重點(diǎn)?。。?!

篇幅太長(zhǎng)了,請(qǐng)看下一章吧?。。?!

讀書感悟

來自《貓的報(bào)恩》

  • 我始終相信,在這個(gè)世界上,一定有另一個(gè)自己,在做著我不敢做的事,在過著我想過的生活。
  • 生命可以隨心所欲,但不能隨波逐流。
  • 當(dāng)人類用感情和希望去創(chuàng)造一樣?xùn)|西,那一樣?xùn)|西就會(huì)被賦予靈魂。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容