日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

stream+springmvc实现文件断点续传

發(fā)布時(shí)間:2025/3/12 c/c++ 13 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stream+springmvc实现文件断点续传 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
手上有個(gè)文件上傳的需求,并且要支持?jǐn)帱c(diǎn)續(xù)傳最好要兼容性好一些,之前用過uploadify這個(gè)jquery上傳插件,但是首先它不支持?jǐn)帱c(diǎn)續(xù)傳而且HTML5版本的竟然要收費(fèi),秉承中國(guó)特色這里就不予考慮了;于是在網(wǎng)上找到了一個(gè)叫 Stream的支持HTML5和Flash并且支持?jǐn)帱c(diǎn)續(xù)傳的這么一個(gè)插件,經(jīng)過一天的嘗試,終于把它整合到項(xiàng)目中去,現(xiàn)勉強(qiáng)能用了后續(xù)再優(yōu)化優(yōu)化應(yīng)該能投入上線,廢話不多上代碼! HTML+JS: <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <base href="<%=basePath%>"> <title>斷點(diǎn)續(xù)傳</title> <link rel="stylesheet" type="text/css" href="<%=basePath%>/statics/thirdparty/stream/css/stream-v1.css"> <script type="text/javascript" src="<%=basePath%>/statics/thirdparty/stream/js/stream-v1.js"></script> </head> <body> <div id="i_select_files"> </div > <div id="i_stream_files_queue"> </div> <button onclick="javascript:_t.upload();">開始上傳</button>|<button onclick="javascript:_t.stop();">停止上傳</button>|<button onclick="javascript:_t.cancel();">取消</button> |<button onclick="javascript:_t.disable();">禁用文件選擇</button>|<button onclick="javascript:_t.enable();">啟用文件選擇</button> |<button onclick="javascript:_t.hideBrowseBlock();">隱藏文件選擇按鈕</button>|<button onclick="javascript:_t.showBrowseBlock();">顯示文件選擇按鈕</button> |<button onclick="javascript:_t.destroy();_t=null;_t=new Stream(config);">銷毀重新生成按鈕</button> <br> Messages: <div id="i_stream_message_container" class="stream-main-upload-box" style="overflow: auto;height:200px;"> </div> </body><script type="text/javascript">/** * 配置文件(如果沒有默認(rèn)字樣,說明默認(rèn)值就是注釋下的值) * 但是,on*(onSelect, onMaxSizeExceed...)等函數(shù)的默認(rèn)行為 * 是在ID為i_stream_message_container的頁(yè)面元素中寫日志 */ var config = { browseFileId : "i_select_files", /** 選擇文件的ID, 默認(rèn): i_select_files */ browseFileBtn : "<div>請(qǐng)選擇文件</div>", /** 顯示選擇文件的樣式, 默認(rèn): `<div>請(qǐng)選擇文件</div>` */ dragAndDropArea: "i_select_files", /** 拖拽上傳區(qū)域,Id(字符類型"i_select_files")或者DOM對(duì)象, 默認(rèn): `i_select_files` */ dragAndDropTips: "<span>可以把文件(文件夾)拖拽到這里</span>", /** 拖拽提示, 默認(rèn): `<span>把文件(文件夾)拖拽到這里</span>` */ filesQueueId : "i_stream_files_queue", /** 文件上傳容器的ID, 默認(rèn): i_stream_files_queue */ filesQueueHeight : 200, /** 文件上傳容器的高度(px), 默認(rèn): 450 */ messagerId : "i_stream_message_container", /** 消息顯示容器的ID, 默認(rèn): i_stream_message_container */ maxSize: 4294967296, /** 單個(gè)文件的最大大小,默認(rèn):2G */ multipleFiles: false, /** 多個(gè)文件一起上傳, 默認(rèn): false */ autoUploading: true, /** 選擇文件后是否自動(dòng)上傳, 默認(rèn): true */// autoRemoveCompleted : true, /** 是否自動(dòng)刪除容器中已上傳完畢的文件, 默認(rèn): false */// retryCount : 5, /** HTML5上傳失敗的重試次數(shù) */// postVarsPerFile : { /** 上傳文件時(shí)傳入的參數(shù),默認(rèn): {} */// param1: "val1",// param2: "val2"// },// swfURL : "/swf/FlashUploader.swf", /** SWF文件的位置 */ tokenURL : "<%=request.getContextPath()%>/tk", /** 根據(jù)文件名、大小等信息獲取Token的URI(用于生成斷點(diǎn)續(xù)傳、跨域的令牌) */// frmUploadURL : "/fd;", /** Flash上傳的URI */ uploadURL : "<%=request.getContextPath()%>/upload", /** HTML5上傳的URI */// simLimit: 200, /** 單次最大上傳文件個(gè)數(shù) */// extFilters: [".txt", ".rpm", ".rmvb", ".gz", ".rar", ".zip", ".avi", ".mkv", ".mp3"], /** 允許的文件擴(kuò)展名, 默認(rèn): [] */// onSelect: function(list) {alert('onSelect')}, /** 選擇文件后的響應(yīng)事件 */ onMaxSizeExceed: function(size, limited, name) {alert('文件已超過4G');}, /** 文件大小超出的響應(yīng)事件 */// onFileCountExceed: function(selected, limit) {alert('onFileCountExceed')}, /** 文件數(shù)量超出的響應(yīng)事件 */// onExtNameMismatch: function(name, filters) {alert('onExtNameMismatch')}, /** 文件的擴(kuò)展名不匹配的響應(yīng)事件 */// onCancel : function(file) {alert('Canceled: ' + file.name)}, /** 取消上傳文件的響應(yīng)事件 */// onComplete: function(file) {alert('onComplete')}, /** 單個(gè)文件上傳完畢的響應(yīng)事件 */ onQueueComplete: function() { alert('onQueueComplete'); }, /** 所以文件上傳完畢的響應(yīng)事件 */// onUploadError: function(status, msg) {alert('onUploadError')} /** 文件上傳出錯(cuò)的響應(yīng)事件 */// onDestroy: function() {alert('onDestroy')} /** 文件上傳出錯(cuò)的響應(yīng)事件 */ }; var _t = new Stream(config);</script></html> 頁(yè)面代碼精簡(jiǎn)了下,只留下了關(guān)鍵部分。 TokenServlet: package com.dnion.oa.stream.servlet;?import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.lang.StringUtils;import org.json.JSONException;import org.json.JSONObject;import com.dnion.oa.stream.util.IoUtil;import com.dnion.oa.stream.util.TokenUtil;import com.dnion.oa.utils.Configuration;import com.dnion.oa.utils.Constant;?/** * According the file name and its size, generate a unique token. And this * token will be refer to user's file. */public class TokenServlet extends HttpServlet { private static final long serialVersionUID = 2650340991003623753L; static final String FILE_NAME_FIELD = "name"; static final String FILE_SIZE_FIELD = "size"; static final String TOKEN_FIELD = "token"; static final String SERVER_FIELD = "server"; static final String SUCCESS = "success"; static final String MESSAGE = "message"; @Override public void init() throws ServletException { }? @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = StringUtils.trimToEmpty(req.getParameter(FILE_NAME_FIELD)); String size = StringUtils.trimToEmpty(req.getParameter(FILE_SIZE_FIELD)); String token = TokenUtil.generateToken(name, size); PrintWriter writer = resp.getWriter(); JSONObject json = new JSONObject(); try { if (name.equals("")) { json.put(SUCCESS, false); json.put(MESSAGE, "文件名為空"); }else if (size.equals("")) { json.put(SUCCESS, false); json.put(MESSAGE, "文件大小為空"); }else{ json.put(TOKEN_FIELD, token); if (Configuration.getInstance().getIsCrossed()) json.put(SERVER_FIELD, Configuration.getInstance().getCrossServer()); json.put(SUCCESS, true); json.put(MESSAGE, "獲取TOKEN成功"); } } catch (JSONException e) { e.printStackTrace(); } /** TODO: save the token. */ writer.write(json.toString()); }? @Override protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doHead(req, resp); }? @Override public void destroy() { super.destroy(); }} 這是實(shí)現(xiàn)上傳功能必須實(shí)現(xiàn)接口之一,看名字就知道基本功能就是獲取token實(shí)現(xiàn)‘?dāng)帱c(diǎn)‘功能 StreamServlet: package com.dnion.oa.stream.servlet;?import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.json.JSONException;import org.json.JSONObject;import com.dnion.oa.stream.util.IoUtil;import com.dnion.oa.utils.Configuration;?/** * File reserved servlet, mainly reading the request parameter and its file * part, stored it. */public class StreamServlet extends HttpServlet { private static final long serialVersionUID = -8619685235661387895L; /** when the has increased to 10kb, then flush it to the hard-disk. */ static final int BUFFER_LENGTH = 10240; static final String START_FIELD = "start"; public static final String CONTENT_RANGE_HEADER = "content-range"; @Override public void init() throws ServletException { } /** * Lookup where's the position of this file? */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doOptions(req, resp); final String token = req.getParameter(TokenServlet.TOKEN_FIELD); final String size = req.getParameter(TokenServlet.FILE_SIZE_FIELD); final String fileName = req.getParameter(TokenServlet.FILE_NAME_FIELD); final PrintWriter writer = resp.getWriter(); /** TODO: validate your token. */ JSONObject json = new JSONObject(); long start = 0; boolean success = true; String message = ""; try { File f = IoUtil.getTokenedFile(token); start = f.length(); /** file size is 0 bytes. */ if (token.endsWith("_0") && "0".equals(size) && 0 == start) f.renameTo(IoUtil.getFile(fileName)); } catch (FileNotFoundException fne) { message = "Error: " + fne.getMessage(); success = false; } finally { try { if (success) json.put(START_FIELD, start); json.put(TokenServlet.SUCCESS, success); json.put(TokenServlet.MESSAGE, message); } catch (JSONException e) {} writer.write(json.toString()); IoUtil.close(writer); } }? @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doOptions(req, resp); final String token = req.getParameter(TokenServlet.TOKEN_FIELD); final String fileName = req.getParameter(TokenServlet.FILE_NAME_FIELD); Range range = IoUtil.parseRange(req); OutputStream out = null; InputStream content = null; final PrintWriter writer = resp.getWriter(); /** TODO: validate your token. */ JSONObject json = new JSONObject(); long start = 0; boolean success = true; String message = ""; File f = IoUtil.getTokenedFile(token); try { if (f.length() != range.getFrom()) { /** drop this uploaded data */ throw new StreamException(StreamException.ERROR_FILE_RANGE_START); } out = new FileOutputStream(f, true); content = req.getInputStream(); int read = 0; final byte[] bytes = new byte[BUFFER_LENGTH]; while ((read = content.read(bytes)) != -1) out.write(bytes, 0, read); start = f.length(); } catch (StreamException se) { success = StreamException.ERROR_FILE_RANGE_START == se.getCode(); message = "Code: " + se.getCode(); } catch (FileNotFoundException fne) { message = "Code: " + StreamException.ERROR_FILE_NOT_EXIST; success = false; } catch (IOException io) { message = "IO Error: " + io.getMessage(); success = false; } finally { IoUtil.close(out); IoUtil.close(content);? /** rename the file */ if (range.getSize() == start) { /** fix the `renameTo` bug */ File dst = IoUtil.getFile(fileName); dst.delete(); f.renameTo(dst); System.out.println("TK: `" + token + "`, NE: `" + fileName + "`"); /** if `STREAM_DELETE_FINISH`, then delete it. */ if (Configuration.getInstance().getIsDeleteFinished()) { dst.delete(); } } try { if (success) json.put(START_FIELD, start); json.put(TokenServlet.SUCCESS, success); json.put(TokenServlet.MESSAGE, message); } catch (JSONException e) {} writer.write(json.toString()); IoUtil.close(writer); } } @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("application/json"); resp.setHeader("Access-Control-Allow-Headers", "Content-Range,Content-Type"); resp.setHeader("Access-Control-Allow-Origin", Configuration.getInstance().getCrossOrigins()); resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); }? @Override public void destroy() { super.destroy(); }} 必須實(shí)現(xiàn)接口之一,也是最為主要的一個(gè)servlet 這兩個(gè)接口其實(shí)就是對(duì)應(yīng)了js代碼中tokenURL和uploadURL,跳轉(zhuǎn)關(guān)系在web.xml中定義,把下面這段加進(jìn)去就行了,記得改路徑 <servlet> <servlet-name>TokenServlet</servlet-name> <servlet-class>com.dnion.oa.stream.servlet.TokenServlet</servlet-class> <load-on-startup>0</load-on-startup></servlet><servlet-mapping> <servlet-name>TokenServlet</servlet-name> <url-pattern>/tk</url-pattern></servlet-mapping><servlet> <servlet-name>StreamServlet</servlet-name> <servlet-class>com.dnion.oa.stream.servlet.StreamServlet</servlet-class> <load-on-startup>0</load-on-startup></servlet><servlet-mapping> <servlet-name>StreamServlet</servlet-name> <url-pattern>/upload</url-pattern></servlet-mapping>?<servlet> <servlet-name>FormDataServlet</servlet-name> <servlet-class>com.dnion.oa.stream.servlet.FormDataServlet</servlet-class> <load-on-startup>0</load-on-startup></servlet><servlet-mapping> <servlet-name>FormDataServlet</servlet-name> <url-pattern>/fd</url-pattern></servlet-mapping> 基本參數(shù)保存在properties文件中 stream-config.properties: # file stored repository (Chinese words need ASCII, help tool @http://tool.oschina.net/encode?type=3)STREAM_FILE_REPOSITORY=# when the file has uploaded, whether delete it.STREAM_DELETE_FINISH=false# this server whether allow other different domain[s] upload file to this serverSTREAM_IS_CROSS=false# allowed domain (PS: flash method need modifying the `crossdomain.xml`)STREAM_CROSS_ORIGIN=*# when Browser @http:www.A.com, the file will upload to @STREAM_CROSS_SERVERSTREAM_CROSS_SERVER=http://customers.duapp.comTEMP_UPLOAD_PATH=E\:\\resumeUpload //暫時(shí)放在本地 測(cè)試用 Configuration.java讀取配置文件 Configuration.java:
package com.dnion.oa.utils;?import org.apache.commons.configuration.ConfigurationException;import org.apache.commons.configuration.PropertiesConfiguration;?public class Configuration { private static Configuration config; /** * 版本號(hào) */ private String version; private String crossServer; private String crossOrigins; private String dnionTranscoderUrl; private Boolean isDeleteFinished; private Boolean isCrossed; private String uploadPath; private Configuration() { }? public synchronized static Configuration getInstance() { if (config == null) { config = new Configuration(); config.init(); } return config; } /** * 初始化 */ private void init() { PropertiesConfiguration prop = new PropertiesConfiguration(); prop.setEncoding("utf-8"); try { prop.load("props/stream-config.properties"); this.setCrossServer(prop.getString("STREAM_CROSS_SERVER", "")); this.setCrossOrigins(prop.getString("STREAM_CROSS_ORIGIN", "")); this.setDnionTranscoderUrl(prop.getString("DNION_TRANS_CODER_URL", "")); this.setIsDeleteFinished(Boolean.valueOf(prop.getString( "STREAM_DELETE_FINISH", ""))); this.setIsCrossed(Boolean.valueOf(prop.getString("STREAM_IS_CROSS", ""))); this.setChownShellCmd(prop.getString("CHOWN_SHELL_CMD", "")); this.setUploadPath(prop.getString("TEMP_UPLOAD_PATH", "")); } catch (ConfigurationException e) { e.printStackTrace(); } }? public String getVersion() { return version; }? public void setVersion(String version) { this.version = version; }? ...getter and setters 還有6對(duì),別忘了} Maven依賴: <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version></dependency><dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version></dependency><dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20090211</version></dependency><dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.9</version></dependency> 其他還有一些TokenUtil.java就不給出了,作者代碼托管在oschina上,自己拉了看著改吧~ 效果圖稍候放出:



總結(jié)

以上是生活随笔為你收集整理的stream+springmvc实现文件断点续传的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。