@Component("javaLargeFileUploaderServlet")
創(chuàng)新互聯(lián)建站專注于澧縣企業(yè)網(wǎng)站建設(shè),自適應網(wǎng)站建設(shè),商城網(wǎng)站定制開發(fā)。澧縣網(wǎng)站建設(shè)公司,為澧縣等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站設(shè)計,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)
@WebServlet(name = "javaLargeFileUploaderServlet", urlPatterns = { "/javaLargeFileUploaderServlet" })
public class UploadServlet extends HttpRequestHandlerServlet
implements HttpRequestHandler {
private static final Logger log = LoggerFactory.getLogger(UploadServlet.class);
@Autowired
UploadProcessor uploadProcessor;
@Autowired
FileUploaderHelper fileUploaderHelper;
@Autowired
ExceptionCodeMappingHelper exceptionCodeMappingHelper;
@Autowired
Authorizer authorizer;
@Autowired
StaticStateIdentifierManager staticStateIdentifierManager;
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws IOException {
log.trace("Handling request");
Serializable jsonObject = null;
try {
// extract the action from the request
UploadServletAction actionByParameterName =
UploadServletAction.valueOf(fileUploaderHelper.getParameterValue(request, UploadServletParameter.action));
// check authorization
checkAuthorization(request, actionByParameterName);
// then process the asked action
jsonObject = processAction(actionByParameterName, request);
// if something has to be written to the response
if (jsonObject != null) {
fileUploaderHelper.writeToResponse(jsonObject, response);
}
}
// If exception, write it
catch (Exception e) {
exceptionCodeMappingHelper.processException(e, response);
}
}
private void checkAuthorization(HttpServletRequest request, UploadServletAction actionByParameterName)
throws MissingParameterException, AuthorizationException {
// check authorization
// if its not get progress (because we do not really care about authorization for get
// progress and it uses an array of file ids)
if (!actionByParameterName.equals(UploadServletAction.getProgress)) {
// extract uuid
final String fileIdFieldValue = fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId, false);
// if this is init, the identifier is the one in parameter
UUID clientOrJobId;
String parameter = fileUploaderHelper.getParameterValue(request, UploadServletParameter.clientId, false);
if (actionByParameterName.equals(UploadServletAction.getConfig) parameter != null) {
clientOrJobId = UUID.fromString(parameter);
}
// if not, get it from manager
else {
clientOrJobId = staticStateIdentifierManager.getIdentifier();
}
// call authorizer
authorizer.getAuthorization(
request,
actionByParameterName,
clientOrJobId,
fileIdFieldValue != null ? getFileIdsFromString(fileIdFieldValue).toArray(new UUID[] {}) : null);
}
}
private Serializable processAction(UploadServletAction actionByParameterName, HttpServletRequest request)
throws Exception {
log.debug("Processing action " + actionByParameterName.name());
Serializable returnObject = null;
switch (actionByParameterName) {
case getConfig:
String parameterValue = fileUploaderHelper.getParameterValue(request, UploadServletParameter.clientId, false);
returnObject =
uploadProcessor.getConfig(
parameterValue != null ? UUID.fromString(parameterValue) : null);
break;
case verifyCrcOfUncheckedPart:
returnObject = verifyCrcOfUncheckedPart(request);
break;
case prepareUpload:
returnObject = prepareUpload(request);
break;
case clearFile:
uploadProcessor.clearFile(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)));
break;
case clearAll:
uploadProcessor.clearAll();
break;
case pauseFile:
ListUUID uuids = getFileIdsFromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId));
uploadProcessor.pauseFile(uuids);
break;
case resumeFile:
returnObject =
uploadProcessor.resumeFile(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)));
break;
case setRate:
uploadProcessor.setUploadRate(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)),
Long.valueOf(fileUploaderHelper.getParameterValue(request, UploadServletParameter.rate)));
break;
case getProgress:
returnObject = getProgress(request);
break;
}
return returnObject;
}
ListUUID getFileIdsFromString(String fileIds) {
String[] splittedFileIds = fileIds.split(",");
ListUUID uuids = Lists.newArrayList();
for (int i = 0; i splittedFileIds.length; i++) {
uuids.add(UUID.fromString(splittedFileIds[i]));
}
return uuids;
}
private Serializable getProgress(HttpServletRequest request)
throws MissingParameterException {
Serializable returnObject;
String[] ids =
new Gson()
.fromJson(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId), String[].class);
CollectionUUID uuids = Collections2.transform(Arrays.asList(ids), new FunctionString, UUID() {
@Override
public UUID apply(String input) {
return UUID.fromString(input);
}
});
returnObject = Maps.newHashMap();
for (UUID fileId : uuids) {
try {
ProgressJson progress = uploadProcessor.getProgress(fileId);
((HashMapString, ProgressJson) returnObject).put(fileId.toString(), progress);
}
catch (FileNotFoundException e) {
log.debug("No progress will be retrieved for " + fileId + " because " + e.getMessage());
}
}
return returnObject;
}
private Serializable prepareUpload(HttpServletRequest request)
throws MissingParameterException, IOException {
// extract file information
PrepareUploadJson[] fromJson =
new Gson()
.fromJson(fileUploaderHelper.getParameterValue(request, UploadServletParameter.newFiles), PrepareUploadJson[].class);
// prepare them
final HashMapString, UUID prepareUpload = uploadProcessor.prepareUpload(fromJson);
// return them
return Maps.newHashMap(Maps.transformValues(prepareUpload, new FunctionUUID, String() {
public String apply(UUID input) {
return input.toString();
};
}));
}
private Boolean verifyCrcOfUncheckedPart(HttpServletRequest request)
throws IOException, MissingParameterException, FileCorruptedException, FileStillProcessingException {
UUID fileId = UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId));
try {
uploadProcessor.verifyCrcOfUncheckedPart(fileId,
fileUploaderHelper.getParameterValue(request, UploadServletParameter.crc));
}
catch (InvalidCrcException e) {
// no need to log this exception, a fallback behaviour is defined in the
// throwing method.
// but we need to return something!
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
使用Struts2上傳文件:
Struts文件上傳需要使用File Upload Filter。Filter Upload Filter使用一些默認的規(guī)則:
Form中的s:file name="image"/s:file標簽對應著Action類中的三個屬性分別是:上傳文件(java.io.File類型),文件名(java.lang.String類型),文件類型(java.lang.String類型,例如:image/jpeg)。命名規(guī)約為:
文件:名字與s:file標簽中的name屬性一致,這里為:image
文件名:文件 + FileName,這里為:imageFileName
文件類型:文件 + ContentType,這里為:imageContentType
所以針對上述s:file name="image"/s:file表示啊的上傳文件的JSP和Action類被別為:
imageUpload.jsp:
[html]?view plain?copy
%@?page?contentType="text/html;charset=UTF-8"?language="java"?%
%@taglib?prefix="s"?uri="/struts-tags"?%
html
headtitleImage?Upload/title/head
body
h1?Image?Upload?Page?/h1
s:form?action="imageUpload"?method="post"?enctype="multipart/form-data"
s:file?name="image"/s:file
s:submit/s:submit
/s:form
/body
/html
ImageUploadAction.java:
[html]?view plain?copy
package?com.jpleasure;
import?com.opensymphony.xwork2.ActionSupport;
import?java.io.File;
import?java.io.InputStream;
import?java.io.FileInputStream;
import?java.io.FileNotFoundException;
public?class?ImageUploadAction?extends?ActionSupport?{
private?File?image;
private?String?imageFileName;
private?String?imageContentType;
public?File?getImage()?{
return?image;
}
public?void?setImage(File?image)?{
this.image?=?image;
}
public?String?getImageFileName()?{
return?imageFileName;
}
public?void?setImageFileName(String?imageFileName)?{
this.imageFileName?=?imageFileName;
}
public?String?getImageContentType()?{
return?imageContentType;
}
public?void?setImageContentType(String?imageContentType)?{
this.imageContentType?=?imageContentType;
}
public?String?execute()?{
if?(image?!=?null)?{
System.out.println("file?name?is:"?+?this.imageFileName);
System.out.println("file?content?type?is:"?+?this.imageContentType);
System.out.println("file?length?is:"?+?this.image.length());
}
return?SUCCESS;
}
}
Struts.xml配置文件:
[html]?view plain?copy
action?name="imageUpload"?class="com.jpleasure.ImageUploadAction"
result/success.jsp/result
/action
這樣當我們選中上傳文件,提交的時候:文件內(nèi)容會以File類型的方式放在image聲明的變量中。文件的名字將會被放在imageFileName命名的變量中,文件的類型被放在imageContentType命名的變量中。
文件下載:
文件下載需要使用一個特殊的Result,stream類型的Result。Stream類型的Result主要用來處理文件下載操作。
處理原理為:所有的下載文件都是將一個二進制的流寫入到HttpResponse中去。在Action類中定義一個InputSream類型的二進制流,在Result返回給用戶的時候返回給用戶。
擴展上述的代碼,將上傳來的文件直接下載給用戶:
ImageUploadAction中需要追加一個InputSream類型的對象,并且指向上傳的文件,代碼如下,紅色部分表示變化:
[html]?view plain?copy
package?com.jpleasure;
import?com.opensymphony.xwork2.ActionSupport;
import?java.io.File;
import?java.io.InputStream;
import?java.io.FileInputStream;
import?java.io.FileNotFoundException;
public?class?ImageUploadAction?extends?ActionSupport?{
private?File?image;
private?String?imageFileName;
private?String?imageContentType;
private?InputStream?imageInputStream?=?null;
public?InputStream?getImageInputStream()?{
return?imageInputStream;
}
public?void?setImageInputStream(InputStream?imageInputStream)?{
this.imageInputStream?=?imageInputStream;
}
public?File?getImage()?{
return?image;
}
public?void?setImage(File?image)?{
this.image?=?image;
}
public?String?getImageFileName()?{
return?imageFileName;
}
public?void?setImageFileName(String?imageFileName)?{
this.imageFileName?=?imageFileName;
}
public?String?getImageContentType()?{
return?imageContentType;
}
public?void?setImageContentType(String?imageContentType)?{
this.imageContentType?=?imageContentType;
}
public?String?execute()?{
if?(image?!=?null)?{
System.out.println("file?name?is:"?+?this.imageFileName);
System.out.println("file?content?type?is:"?+?this.imageContentType);
System.out.println("file?length?is:"?+?this.image.length());
try?{
this.imageInputStream?=?new?FileInputStream?(image);
}?catch?(FileNotFoundException?e)?{
e.printStackTrace();
}
}
return?SUCCESS;
}
}
配置文件為,紅色為變化部分:
[html]?view plain?copy
action?name="imageUpload"?class="com.jpleasure.ImageUploadAction"
result?name="success"?type="stream"
param?name="contentType"image/pjpeg/param
param?name="inputName"imageInputStream/param
param?name="contentDisposition"attachment;filename="image.jpg"/param
param?name="bufferSize"1024/param
/result
/action
ContentType表示下載文件的類型。
InputName表示Action類中用來下載文件的字段的名字。
ContentDisposition用來控制文件下載的一些信息,包括是否打開另存對話框,下載文件名等。
BufferSize表示文件下載時使用的緩沖區(qū)的大小。
實際項目開發(fā)的考慮:
控制上傳文件的類型和最大允許上傳文件的size
使用File Upload Intercepter的參數(shù)可盈控制上傳文件的類型和最大允許上傳文件的size。例如:
[html]?view plain?copy
struts
package?name="myPackage"?extends="struts-default"
interceptor-ref?name="fileUpload"
param?name="maximumSize"2MB/param
param?name="allowedTypes"text/html,image/jpeg/param
/interceptor-ref
interceptor-ref?name="basicStack"/
action?name="imageUpload"?class="com.jpleasure.ImageUploadAction"
result?name="success"?type="stream"
param?name="contentType"image/pjpeg/param
param?name="inputName"imageInputStream/param
param?name="contentDisposition"
attachment;filename="image.jpg"
/param
param?name="bufferSize"1024/param
/result
/action
/package
/struts
上述表示允許上傳jpeg和html類型的文件,且最大文件上傳size為2MB
顯示錯誤信息:
可以使用如下key表示的message來顯示文件上傳出錯的提示信息:
消息Key ? ?說明 ?
struts.messages.error.uploading ? ?文件無法正常上傳時的公共錯誤 ?
struts.messages.error.file.too.large ? ?文件大小超過最大允許size時的錯誤提示 ?
struts.messages.error.content.type.not.allowed ? ?文件類型不在上傳文件允許類型中的錯誤提示 ?
//嘗試移動文件內(nèi)讀取指針,實現(xiàn)斷點續(xù)傳
result
=
uploadFile(remoteFileName,
f,
ftpClient,
remoteSize);
//如果斷點續(xù)傳沒有成功,則刪除服務(wù)器上文件,重新上傳
if(result
==
UploadStatus.Upload_From_Break_Failed){
if(!ftpClient.deleteFile(remoteFileName)){
return
UploadStatus.Delete_Remote_Faild;
}
result
=
uploadFile(remoteFileName,
f,
ftpClient,
0);
}
024字節(jié))。第一次B接收了512字節(jié),那么第二次連接A就應該從513字節(jié)開始傳輸。
也就是說,在第二次傳輸時,B要提供“我要從513字節(jié)開始傳送文件F”的信息,然后A使用FileInputStream構(gòu)建輸入流讀取本地文件,使用skip(512)方法跳過文件F的前512字節(jié)再傳送文件,之后B將數(shù)據(jù)追加(append)到先前接收的文件末尾即可。
進一步考慮,如果要實現(xiàn)多線程傳送,即分塊傳輸,也同樣的道理。假如B要求分作兩塊同時傳輸,那么A啟動兩個線程,一個從513字節(jié)讀到768字節(jié)(工256字節(jié)),第二個線程從769字節(jié)到1024字節(jié)即可。
如果你要從網(wǎng)絡(luò)上下載文件,就是說A方不是你實現(xiàn)的,那么你要先確認A方支不支持斷電續(xù)傳功能(HTTP1.1),然后你查閱下HTTP1.1協(xié)議,在HTTP1.1版本里,可以通過設(shè)置請求包頭某個字段的信息(使用URLConnection創(chuàng)建連接并使用setRequestProperty(String key, String value) 方法設(shè)置)從而精確讀取文件的某一段數(shù)據(jù)的。注意,基于HTTP斷點續(xù)傳的關(guān)鍵是1.1版本,1.0版本是不支持的。
補充:
嗯,查到了,是設(shè)置range屬性,即setRequestProperty("range", "bytes=513-1024").你可以使用迅雷下載某個文件,然后從”線程信息“中就可以看到這個http1.1斷點續(xù)傳的所有行為信息了。
1package com.tangshun.;
2
3import java.io.File;
4import java.io.IOException;
5import java.io.InputStream;
6import java.io.RandomAccessFile;
7import java.net.HttpURLConnection;
8import java.net.MalformedURLException;
9import java.net.URL;
10
11//斷點續(xù)傳
12public class DownLoad {
13
14 public static void down(String URL, long nPos, String savePathAndFile) {
15 try {
16 URL url = new URL(URL);
17 HttpURLConnection httpConnection = (HttpURLConnection) url
18 .openConnection();
19 // 設(shè)置User-Agent
20 httpConnection.setRequestProperty("User-Agent", "NetFox");
21 // 設(shè)置斷點續(xù)傳的開始位置
22 httpConnection.setRequestProperty("RANGE", "bytes=" + nPos);
23 // 獲得輸入流
24 InputStream input = httpConnection.getInputStream();
25 RandomAccessFile oSavedFile = new RandomAccessFile(savePathAndFile,
26 "rw");
27 // 定位文件指針到nPos位置
28 oSavedFile.seek(nPos);
29 byte[] b = new byte[1024];
30 int nRead;
31 // 從輸入流中讀入字節(jié)流,然后寫到文件中
32 while ((nRead = input.read(b, 0, 1024)) 0) {
33 (oSavedFile).write(b, 0, nRead);
34 }
35 httpConnection.disconnect();
36 } catch (MalformedURLException e) {
37 e.printStackTrace();
38 } catch (IOException e) {
39 e.printStackTrace();
40 }
41 }
42
43 public static long getRemoteFileSize(String url) {
44 long size = 0;
45 try {
46 HttpURLConnection conn = (HttpURLConnection) (new URL(url))
47 .openConnection();
48 size = conn.getContentLength();
49 conn.disconnect();
50 } catch (Exception e) {
51 e.printStackTrace();
52 }
53 return size;
54 }
55
56public static void main(String[] args) {
57 String url = " ";
58 String savePath = "F:\\";
59 String fileName = url.substring(url.lastIndexOf("/"));
60 String fileNam=fileName;
61 HttpURLConnection conn = null;
62 try {
63 conn = (HttpURLConnection) (new URL(url)).openConnection();
64 } catch (Exception e) {
65 e.printStackTrace();
66 }
67 File file = new File(savePath + fileName);
68 // 獲得遠程文件大小
69 long remoteFileSize = getRemoteFileSize(url);
70 System.out.println("遠程文件大小="+remoteFileSize);
71 int i = 0;
72 if (file.exists()) {
73 // 先看看是否是完整的,完整,換名字,跳出循環(huán),不完整,繼續(xù)下載
74 long localFileSize = file.length();
75 System.out.println("已有文件大小為:"+localFileSize);
76
77 if (localFileSize remoteFileSize) {
78 System.out.println("文件續(xù)傳");
79 down(url, localFileSize, savePath + fileName);
80 }else{
81 System.out.println("文件存在,重新下載");
82 do{
83 i++;
84 fileName = fileNam.substring(0, fileNam.indexOf(".")) + "(" + i
85 + ")" + fileNam.substring(fileNam.indexOf("."));
86
87 file = new File(savePath + fileName);
88 }while(file.exists());
89 try {
90 file.createNewFile();
91 } catch (IOException e) {
92 e.printStackTrace();
93 }
94 down(url, 0, savePath + fileName);
95 }
96 // 下面表示文件存在,改名字
97
98 } else {
99 try {
100 file.createNewFile();
101 System.out.println("下載中");
102 down(url, 0, savePath + fileName);
103 } catch (IOException e) {
104 e.printStackTrace();
105 }
106 }
107 }}
108
斷點設(shè)置方法,當有臨時文件時,直接在臨時文件中讀取上次下載中斷時的斷點位置。沒有臨時文件,即第一次下載時,重新設(shè)置斷點。 rantmpfile.seek()跳轉(zhuǎn)到一個位置的目的是為了讓各個斷點存儲的位置盡量分開。