web大文件上傳(web應用---SSH框架)

struts提供的上傳文件功能,文件太大就報錯。javascript

須要解決超大文件(幾個G)上傳的問題。斷點續傳的功能。css

技術選型,查百度以後。最終選擇了這個stream插件。html

stream官方資料地址:http://www.twinkling.cn/java

去下載源碼。源碼地址。
項目源碼託管在git.oschina.net,地址:http://git.oschina.net/jiangdx/streamjquery

 

 

解壓安裝包以後,在eclipse中導入此項目。(注,這是一個Maven項目,服務器用的是tomcat 不是jettygit

(若是沒有maven項目選項,eclipse須要安裝maven插件,詳情見其餘文章)web

 

以下圖配置 maven命令, 點擊Run  就會啓動項目ajax

 

日誌能夠看到[INFO] Running war on http://localhost:8080/stream數據庫

 

瀏覽器地址:http://localhost:8080/stream    就能夠訪問項目根目錄(src/main/webapp目錄)json

 好比訪問 webapp目錄中的s1.html

http://localhost:8080/stream/s1.html

就能夠看到示例了

 

 

下一步:如何整合到我本身的項目中(項目使用的是SSH

借鑑了方塊糖的頁面。方塊糖這個是整合到SpringMVC的。而不是SSH 

首先,選定一個樣式,我選擇的是以下的樣式,在這個基礎上進行更改。命名爲streamDemo.jsp  (html轉爲jsp格式,你能夠在你項目中複製一個jsp的文件,而後將代碼選擇性的複製過來)

streamDemo.jsp借鑑了方塊糖的頁面,將頁面代碼拷貝過來。拷貝地址:http://www.fangkuaitang.net/?p=2164

此時,須要導入兩個文件:

這個頁面能夠看到,須要引用的文件stream-v1.css  和  stream-v1.js

(在Maven項目源碼中本身找一下。而後複製到本身的項目中的對應位置)

<link type="text/css" rel="stylesheet"  href="${ctx}/css/stream-v1.css"  />

<script type="text/javascript" src="${ctx}/javascript/stream-v1.js"></script>  

${ctx}是項目的根目錄。請根據本身項目的實際的文件位置,相應更改,以保證streamDemo.jsp中能夠引用到。

 

 

這個streamDemo.jsp中最重要的兩個url

 

tokenURL : "${ctx}/servlet/tk.servlet", /** 根據文件名、大小等信息獲取TokenURI(用於生成斷點續傳、跨域的令牌) */

 

uploadURL : "${ctx}/servlet/upload.servlet", /** HTML5上傳的URI */

 

 

 

這個Maven項目源碼,後臺使用的是servlet  而咱們項目SSH使用的是struts

 

這裏就涉及到了,servletstruts的共存問題

 

 

 

!!此時先將後臺代碼加入到本身的項目中,在streamMaven項目中,直接將cn.twinkling.stream包下的全部文件拷貝到本身的項目中,放置到src中。

 

爲了方便,包的名字也沒有更改。

 

此時還須要引入必須的jar

引入相關依賴包: commons-fileupload-1.3.jar

 commons-io-2.2.jar

json-20090211.jar(這個json可能還須要其餘的jar包,若是報錯,百度一下,加上便可。)

引入 stream-*.jar

(能夠從maven的本地倉庫中獲取到jar

C:\Users\Administrator\.m2\repository 中找)

 實在找不到的,直接百度找。。。。

 

到此爲止,前臺頁面有了。後臺代碼也有了。Jar包已經buildpath 完畢。

 

是時候配置web.xml,打通前臺和後臺了。

 

前面提到:這個Maven項目源碼,後臺使用的是servlet  而咱們項目SSH使用的是struts

這裏就涉及到了,servletstruts的共存問題

http://blog.csdn.net/huilangeliuxin/article/details/10495403

 

使用第一種方式。方式一:修改servlet的相關配置,統一在servlet後面加上「.servlet」

 借鑑stream  Maven項目源碼中的web.xml中的配置。對應修改本身項目的web.xml

在其中加上。

<!-- fileUpload -->
 <servlet>
        <servlet-name>TokenServlet</servlet-name>
        <servlet-class>cn.twinkling.stream.servlet.TokenServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>TokenServlet</servlet-name>
        <url-pattern>/servlet/tk.servlet</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>StreamServlet</servlet-name>
        <servlet-class>cn.twinkling.stream.servlet.StreamServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>StreamServlet</servlet-name>
        <url-pattern>/servlet/upload.servlet</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>FormDataServlet</servlet-name>
        <servlet-class>cn.twinkling.stream.servlet.FormDataServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>FormDataServlet</servlet-name>
        <url-pattern>/servlet/fd.servlet</url-pattern>
    </servlet-mapping>
    <!-- fileUpload -->

 

再修改streamDemo.jsp中的URL ,和上面web.xml中的配置相對應。 

tokenURL : "${ctx}/servlet/tk.servlet", /** 根據文件名、大小等信息獲取TokenURI(用於生成斷點續傳、跨域的令牌) */

uploadURL : "${ctx}/servlet/upload.servlet", /** HTML5上傳的URI */

-----------

Stream Maven項目中src/main/resources中的配置文件stream-config.properties應該複製到本身的項目中,複製到哪裏呢?!

struts.xml同一級別(最後都編譯到classes中。通過測試放置到WEB-INF中是不行的)

stream-config.properties的配置。放置的位置問題

在這個配置文件中,你能夠指定上傳文件存放的地址。

#STREAM_FILE_REPOSITORY=D\:\\XXX

會保存到D盤的 XXX文件夾,若是沒有這個文件夾,就會自動建立。

Configurations.java中會讀取這個配置文件。

 

到這裏,你能夠用debug模式運行,並在cn.twinkling.stream.servlet.TokenServlet

cn.twinkling.stream.servlet.StreamServlet

中打上斷點。進行進一步的調試了。

 

可是還不夠!!!!!咱們要根據本身的需求進一步更改源碼,以符合咱們項目的實際需求!!

 

項目實際需求:

1、咱們須要在文件上傳完畢的時候,同時保存一條記錄到數據庫中。

(包括文件的名字,上傳的時間,上傳的類型,等等)

2、文件是明文存儲的,沒有加密,因此須要加密

3、文件若是名字重複的時候,是不能讓其上傳的。

4、顏色樣式進一步調整。

5、拖拽,更改成【選擇文件】按鈕。

6、加上備註!!

7、文件上傳按月份建立不一樣的文件夾。

---------------------------------------試卷的詳細頁面,點擊【超大資料】按鈕跳轉到大文件上傳頁面----------------------------------------------------

此項目是一個關於試卷的系統,詳細頁面就是一個考試的試卷相關信息。

每一個試卷都有一個id   

下面的id=${model.id} 就是試卷的id

 

aseInfoaction的名字,editBigFileaction中的方法

 

詳情頁面,點擊【超大資料】按鈕

<input name="" type="button" class="btn" value="超大資料" onclick="window.location.href='${ctx}/case/caseInfo!editBigFile?id=${model.id}'"/>

 

 

public class CaseInfoAction{
    protected static final String STREAMFILE = "streamFile";// 上傳超大附件頁面

    /**進入超大附件上傳頁面jzk*/
    public String editBigFile() {
        caseInfo = caseInfoService.getById(caseInfo.getId());
        ServletActionContext.getRequest().setAttribute("caseInfo", caseInfo);
        return 「STREAMFILE 」;//和struts.xml中的配置對應
    }        
}

 

Struts.xml中配置上

<result name="streamFile">/view/case/{1}/streamFile.jsp</result> 

跳轉到streamFile.jsp中  (還看不懂的,複習一下struts2

---------------------------------跳轉到大文件上傳頁面 end-------------------------------------------------

新建一個streamFile.jsp

 

streamFile.jsp在原來streamDemo.jsp基礎上進行更改

頁面中的js

 

okenURL : "${ctx}/servlet/tk.servlet", /** 根據文件名、大小等信息獲取TokenURI(用於生成斷點續傳、跨域的令牌) */

 

tokenURL不用改

 

uploadURL : "${ctx}/servlet/upload.servlet ", /** HTML5上傳的URI */

 

uploadURL不用改

 

 

StreamServlet.java中

 

這個doPost方法中

 

1File f = new File(「D://xxxx/20160303/」);   實現,按月份建不一樣文件夾

 

2、文件的名字加密,更改文件名。 實現文件加密

 

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
File f = IoUtil.getTokenedFile(token);
/**
     * Acquired the file.
     * @param key
     * @return
     * @throws IOException 
     */
    public static File getTokenedFile(String key) throws IOException {
        if (key == null || key.isEmpty())
            return null;

        File f = new File(Configurations.getFileRepository() + File.separator + key);
        if (!f.getParentFile().exists())
            f.getParentFile().mkdirs();
        if (!f.exists())
            f.createNewFile();
        
        return f;
    }

 

Configurations.getFileRepository() 注意這個,

更改成以下:(實現了,按月份創建不一樣的文件夾,

Constants.FILE_UPLOAD_PATH是系統的全局配置。D://

因此getConfig("STREAM_FILE_REPOSITORY")能夠配置爲相對路徑,保存在數據庫中/xxxx/)

 

 

Configurations.java中

 

 

 

public static String getFileRepository() {
        String val =Constants.FILE_UPLOAD_PATH+getConfig("STREAM_FILE_REPOSITORY")+DateTimeUtil.format("yyyyMM",new Date());
        if (val == null || val.isEmpty())
            val = REPOSITORY;
        return val;
    }
同時加上以下這個,這個地址用來保存到數據中,是相對路徑//獲取保存到數據庫中的地址,相對路徑。
    public static String getFileRepositorySaveToDataBase(String fileName) {
        String val = getConfig("STREAM_FILE_REPOSITORY")+DateTimeUtil.format("yyyyMM",new Date())+"/"+PasswordHash.encrypt(fileName.replaceAll("/", Matcher.quoteReplacement(File.separator)));
        if (val == null || val.isEmpty())
            val = REPOSITORY;
        return val;
    }

 

 

名字加密:

dopost

 

/** rename the file */
            if (range.getSize() == start) {
                /** fix the `renameTo` bug */
                File dst = IoUtil.getFile(fileName);

 

IoUtilgetFile方法。

加上 md5加密

 

String name =  PasswordHash.encrypt(filename.replaceAll("/", Matcher.quoteReplacement(File.separator)));

 

md5加密方法以下:

MD5加密:
public static String encrypt(String inString) {
        try {
            MessageDigest md;
            // MD5, SHA, SHA-1
            md = MessageDigest.getInstance("MD5");
            byte[] output = md.digest(inString.getBytes());
            StringBuffer sb = new StringBuffer(2 * output.length);
            for (int i = 0; i < output.length; ++i) {
                int k = output[i] & 0xFF;
                if (k < 0x10) {
                    sb.append('0');
                }
                sb.append(Integer.toHexString(k));
            }
            return sb.toString();
        } catch (java.security.NoSuchAlgorithmException e) {
        }
        return null;
    }

此時:文件加密,和按照月份建立不一樣的文件夾完畢。

 

咱們須要在文件上傳完畢的時候,同時保存一條記錄到數據庫中。

因爲,文件上傳時候,能夠屢次暫停,開始,因此在後臺很差添加,

StreamFile.jsp

 

onComplete: function(file) {},/** 單個文件上傳完畢的響應事件,在這裏文件保存完畢以後,經過ajax保存到表中 */

 

一個文件上傳完畢以後,就會調用一下這裏,因此能夠在這裏經過ajax來將已經上傳的文件的相關信息保存到數據庫表中。

因此,在這裏改成以下

onComplete: function(file) {
//         console.log(file);
        var fileName = file.name;
        var remark="";
         $("li").each(function(){
             if($(this).find("strong").eq(0).html()==fileName)
             {
                 remark=  $(this).find("#fileRemarkId").eq(0).val();    
             }
           });
        
          $.post('${pageContext.request.contextPath}/mycfs/uploadFile!insertToUploadFile', {fileName:fileName,instanceId:'${model.id}',instanceType:"CaseInfo",remark:remark}, function (text, status) {
             var list = eval(text);
             var type = list[0];
             var state = type.state;
             if(state== true){
                 console.log("上傳完畢,併成功保存到數據庫記錄");
             }
          });
    }, /** 單個文件上傳完畢的響應事件,在這裏文件保存完畢以後,經過ajax保存到表中 */

UploadFileAction.java 代碼

/**
     * 超大文件保存以後,插入一條數據
     * @throws JSONException 
     * */
    public String insertToUploadFile() throws JSONException{
        UploadFile up = new UploadFile();
        String fileName = ServletActionContext.getRequest().getParameter("fileName");
        String instanceIdStr = ServletActionContext.getRequest().getParameter("instanceId");
        Long instanceId =Long.valueOf(instanceIdStr);
        String instanceType = ServletActionContext.getRequest().getParameter("instanceType");
        String remark = ServletActionContext.getRequest().getParameter("remark");
        String fileExt = UpLoadFile.getFileExtension(fileName);
        String fileType = "";//未作,file封裝
        Date createDate = new Date();
        String filePathStr = Configurations.getFileRepositorySaveToDataBase(fileName);
        up.setFileName(fileName);
        up.setFileType(fileType);
        up.setFileExt(fileExt);
        up.setRemark(remark);
        up.setCreateDate(createDate);
        up.setInstanceId(instanceId);
        up.setFilePath(filePathStr);
        up.setInstanceType(instanceType);
        uploadFileService.save(up);
        //保存成功
        boolean flagStr = true;
        StringBuffer sb = new StringBuffer( "[");       
        sb.append( "{state:"+ flagStr);
        sb.append( "}]");
        ActionContext. getContext().put("json", sb.toString());//[{state:false}]
        return "ajax";
    }

數據庫結構

 

可能你已經發現上面的  remark 備註問題了。!!

在stream-v1.js中  sCellFileTemplate  中的後面加上

'<div class="">' +
'<br/>' +
'    添加備註:<input class="scinput1" type="text" id="fileRemarkId" name="fileRemark" />' +
'</div>'

此時從新進入streamFile.jsp中。打開控制檯 火狐下的F12 ………………

就會看到以下DOM結構

若是上傳多個文件,remark名字相同,會重複衝突。

解決:

經過一個jquery循環,每上傳完畢一個文件,執行onComplete函數,

此時上傳的文件名字是知道的,經過循環,若是名字相同,那麼就獲取對應的備註,而後保存。

$("li").each(function(){
      if($( this).find( "strong").eq(0).html()==fileName)
      {
      remark=  $(this).find("#fileRemarkId" ).eq(0).val();   
       }
       });

經過ajax保存的代碼上面已經列出。

文件的名字是否重複的校驗。

//建立一個全局的js變量,//將全部的上傳的文件名字都彙總到這個變量,以|分隔
arrayObjTotal = "";

首先在streamFile.jsp中設置ajax爲同步,等待server處理以後,再向下執行其餘代碼。

$(function(){
    $.ajaxSetup({ async: false });
    });

 

stream-v1.js中的修改

 

如何在js獲取根目錄

//獲取當前網址,如: http://localhost:8080/ems/Pages/Basic/Person.jsp
    var curWwwPath = window.document.location.href;
    //獲取主機地址以後的目錄,如: /ems/Pages/Basic/Person.jsp
    var pathName = window.document.location.pathname;
    var pos = curWwwPath.indexOf(pathName);
    //獲取主機地址,如: http://localhost:8080
    var localhostPath = curWwwPath.substring(0, pos);
    //獲取帶"/"的項目名,如:/ems
    var basePath = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);

//addStreamTask是當你選擇文件以後,就會顯示一行文件。若是有重複的文件名,就不讓其顯示。

 

addStreamTask : function(a) {
booleanFlag = null;//全局標識,可否繼續向下執行。
//              ########################
                 var name = a.get( "name");
                 if(arrayObjTotal!= null&&arrayObjTotal!= ""&&arrayObjTotal.indexOf(name)!=-1){ //包含
                     alert(name+ "名字重複!" );
                      return; //若是重複,不執行下面增長的函數
                } else{
                     arrayObjTotal += name+ "|";
                }
                 //複用,原file.jsp的上傳校驗
                $.post(basePath+ '/mycfs/uploadFile!checkFileNameUnique', {arrayObj:arrayObjTotal}, function (text, status) {
                      var list = eval(text);
                 var type = list[0];
                 var state = type.state;
                 var rname = type.rname;
                 var msg = type.msg;
                 if(state== false){
                      if(msg!= null&&msg== 'haveReapeatMsg'){
                          alert( "有重複的文件名,請重試" );
                     } else{
                           //已經存在,將這個名字去除掉
                          arrayObjTotal = arrayObjTotal.replace(rname+"|" , "" );
                          alert( "文件名【"+rname+"】已經存在,請從新上傳" );
                     }
                     booleanFlag = false;
                      return;
                } else{
                   booleanFlag = true;
                }
                });
//              //若是重複,不執行下面增長的函數
                 if(booleanFlag== false){ return;}
                 //##################
var file_id = a.get("id"), cell_file = fCreateContentEle("<li id='" + file_id + "' class='stream-cell-file'></li>");
            cell_file.innerHTML = this.template;

}

 

/**
      * ajax驗證上傳的附件名是否有重複的。只根據名字過濾
      * @return
      */
     public String checkFileNameUnique (){
            //獲取全部的上傳附件的名稱並以|分隔
           String totalFileName = ServletActionContext.getRequest().getParameter( "arrayObj");
            //360瀏覽器獲取上傳文件名時,會有C:\ fakepath\ 將其去掉
            if(totalFileName!= null){
                totalFileName = totalFileName.replaceAll( "\\\\", "");
                totalFileName = totalFileName.replaceAll("C:fakepath" , "" );
           }
           
            boolean flagStr = false; //默認有重複
           String[] aArray = totalFileName.split( "\\|"); //特殊字符,使用轉義
            if(aArray. length==0){ //都刪除以後,傳過來的是空。返回true
                flagStr= true;
           }
            //判斷一塊兒上傳的是否有重複的名字。(數據庫中沒有這個名字)
           Set<String> set= new HashSet<String>();
            for( int i=0;i<aArray. length;i++){
                set.add(aArray[i]);
           }
            if(aArray. length>set.size()){
                flagStr = false;
                StringBuffer sb = new StringBuffer( "[");       
                sb.append( "{state:"+flagStr);
                sb.append( ",rname:'',msg:'haveReapeatMsg'");
                sb.append( "}]");
                ActionContext. getContext().put("json", sb.toString());
                 return "ajax";
           } //判斷一塊兒上傳的是否有重複的名字。(數據庫中沒有這個名字)END
                
            for( int i=0;i<aArray. length;i++){
//              List<UploadFile> repeatFile = uploadFileDao.findAllBy("fileName",aArray[i] );
                List<UploadFile> repeatFile = uploadFileDao.findAllByFileNameAndNotHistory(aArray[i]);
                 if(repeatFile.size()>0){
                      //一旦存在重複的文件名,當即返回
                     flagStr = false;
                     
                     StringBuffer sb = new StringBuffer( "[");       
                     sb.append( "{state:"+flagStr);
                     sb.append( ",rname:'"+aArray[i]+ "'");
                     sb.append( "}]");
                     ActionContext. getContext().put("json", sb.toString());//[{state:false}]
                      return "ajax";
                } else{
                     flagStr = true;
                }
           }
            //都爲true返回
           StringBuffer sbTrue = new StringBuffer( "[");       
           sbTrue.append( "{state:"+flagStr);
           sbTrue.append( "}]");
           ActionContext. getContext().put("json", sbTrue.toString());//[{state:true}]
            return "ajax";
     }

 

 

全部上傳的文件名都保存在arrayObjTotal

同時上傳兩個文件名字相同,會alert(name+ "名字重複!" );

因此,其中的名字都是不相同的,且以  |  分隔。

此時,若是點擊刪除,此時應該將此名字從arrayObjTotal中刪除掉,以保證再次添加時候,能夠正常校驗。

stream-v1.js中:

'    <a class="stream-cancel" href="javascript:void(0)" onclick="restNameTotalStr(this);">\u5220\u9664</a>'

在stream-v1.js最後面加上

//將全部的上傳的文件名字都彙總到這個變量,以|分隔

arrayObjTotal = "";

//點擊刪除按鈕時,將當前的文件名字從arrayObjTotal,名字總的字符串中去掉。

function restNameTotalStr(a){

  var thisName = a.parentNode.parentNode.childNodes[1].childNodes[0].innerHTML;//獲取當前的文件名字

  arrayObjTotal = arrayObjTotal.replace(thisName+"|", "");

}

當上傳成功以後,接着上傳bug問題。上傳成功以後,將全局變量置空便可。

streamFile.jsp中

onQueueComplete: function(data) {
         //將全部的上傳的文件名字都彙總到這個變量,以|分隔
         //所有上傳完畢。置空
         arrayObjTotal="";
     }, /** 全部文件上傳完畢的響應事件 */

 

樣式調整:

上傳容器高度的調整:

filesQueueHeight : 370, /** 文件上傳容器的高度(px), 默認: 450 */

按鈕樣式根據本身的項目統一便可。

拖拽上傳,更改成按鈕上傳 streamFile.jsp

 

 

<div id="i_select_files">

 

</div>

 

從上面移動到下面:

 

<button class= "btn" id ="i_select_files"></ button>|

 

browseFileBtn : "<div>請選擇文件</div>" ,

 

更改成: browseFileBtn : "選擇文件",    去掉<div>標籤

 

將下面stream-v1.js中下面代碼中的?  "block" 改成""。。不然會換行。

!this.browseFileBlockDisplay && (this.browseFileBlockDisplay = this.startPanel.style.display == "" ? "block" : this.startPanel.style.display);

 

同時更改

stream-v1.css中的stream-browse-drag-files-area  要註釋掉,不然

 

.stream-files-scroll {
    height: 450px;
    overflow: auto;
    width: 980px;/*這裏能夠更改容器的寬度!!高度在stremFile.jsp中的filesQueueHeight : 370, /** 文件上傳容器的高度(px), 默認: 450 */ 
}
stream-main-upload-box {
    width: 980px; /*這裏能夠更改容器的寬度*/
    background-color: #FFFFFF;
    border-style: solid;
    border-width: 1px;
/*     border-color: #50A05B; */
    border-color: #299BE8;/*這裏能夠修改邊框顏色爲藍色*/
    clear: both;
    overflow: hidden;
}

注意:upload.gif  和bgx.png  要對應加上。本身找去吧。

 

關於文件的下載。和刪除。之後再說

 

The end

 

最終效果圖

相關文章
相關標籤/搜索