十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
@Component("javaLargeFileUploaderServlet")
创新互联建站专注于澧县企业网站建设,自适应网站建设,商城网站定制开发。澧县网站建设公司,为澧县等地区提供建站服务。全流程定制网站设计,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务
@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使用一些默认的规则:
Form中的s:file name="image"/s:file标签对应着Action类中的三个属性分别是:上传文件(java.io.File类型),文件名(java.lang.String类型),文件类型(java.lang.String类型,例如:image/jpeg)。命名规约为:
文件:名字与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
这样当我们选中上传文件,提交的时候:文件内容会以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表示文件下载时使用的缓冲区的大小。
实际项目开发的考虑:
控制上传文件的类型和最大允许上传文件的size
使用File Upload Intercepter的参数可盈控制上传文件的类型和最大允许上传文件的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 文件类型不在上传文件允许类型中的错误提示
//尝试移动文件内读取指针,实现断点续传
result
=
uploadFile(remoteFileName,
f,
ftpClient,
remoteSize);
//如果断点续传没有成功,则删除服务器上文件,重新上传
if(result
==
UploadStatus.Upload_From_Break_Failed){
if(!ftpClient.deleteFile(remoteFileName)){
return
UploadStatus.Delete_Remote_Faild;
}
result
=
uploadFile(remoteFileName,
f,
ftpClient,
0);
}
024字节)。第一次B接收了512字节,那么第二次连接A就应该从513字节开始传输。
也就是说,在第二次传输时,B要提供“我要从513字节开始传送文件F”的信息,然后A使用FileInputStream构建输入流读取本地文件,使用skip(512)方法跳过文件F的前512字节再传送文件,之后B将数据追加(append)到先前接收的文件末尾即可。
进一步考虑,如果要实现多线程传送,即分块传输,也同样的道理。假如B要求分作两块同时传输,那么A启动两个线程,一个从513字节读到768字节(工256字节),第二个线程从769字节到1024字节即可。
如果你要从网络上下载文件,就是说A方不是你实现的,那么你要先确认A方支不支持断电续传功能(HTTP1.1),然后你查阅下HTTP1.1协议,在HTTP1.1版本里,可以通过设置请求包头某个字段的信息(使用URLConnection创建连接并使用setRequestProperty(String key, String value) 方法设置)从而精确读取文件的某一段数据的。注意,基于HTTP断点续传的关键是1.1版本,1.0版本是不支持的。
补充:
嗯,查到了,是设置range属性,即setRequestProperty("range", "bytes=513-1024").你可以使用迅雷下载某个文件,然后从”线程信息“中就可以看到这个http1.1断点续传的所有行为信息了。
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//断点续传
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 // 设置User-Agent
20 httpConnection.setRequestProperty("User-Agent", "NetFox");
21 // 设置断点续传的开始位置
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 // 从输入流中读入字节流,然后写到文件中
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 // 先看看是否是完整的,完整,换名字,跳出循环,不完整,继续下载
74 long localFileSize = file.length();
75 System.out.println("已有文件大小为:"+localFileSize);
76
77 if (localFileSize remoteFileSize) {
78 System.out.println("文件续传");
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
断点设置方法,当有临时文件时,直接在临时文件中读取上次下载中断时的断点位置。没有临时文件,即第一次下载时,重新设置断点。 rantmpfile.seek()跳转到一个位置的目的是为了让各个断点存储的位置尽量分开。