我这是使用springboot写的不用导额外的包,如果ssm做的话需要导包可以查看下面这个
上传文件
在input框里加上multiple属性,就可以一个input框一下上传多个文件
这是上传按钮的事件 FormData这一行就这么写就行,参数换成form表单的id,直接就把form表单里所有参数传到后台了,包括上传的文件.
//点击上传
$("#businessLicenseBtn").click(function () { //加一个单击事件按钮
var formd = new FormData($("#userCompleteForm")[0]); //$("#userCompleteForm")[0]:把form表单对象转换为js对象
$.ajax({
url:"${pageContext.request.contextPath}/WitkeyDemandScheme/uploadSchemes",//跳转到后台路径
type:"post",
data:formd,
dataType:"json",
processData:false, //用form表单的enctype对象 这两个属性加上就行了
contentType:false, //默认为true,上传数据转为对象,为false 不转为对象
success:function (data) {
var div1 = $("#div1");
$(data).each(function (a,b) {
div1.append(b+"<input type='button' onclick='deleteScheme("+b+")' value='删除'/><br/>")
})
},
error:function(){
alert("上传失败!")
}
})
})
这是后台上传文件的代码,跟上传单个文件一样,只不过,sql语句使用foreach变量就好了,这里面逻辑就是把文件名字多加了点避免上传上去的文件名重复.
//批量上传文件
@RequestMapping("/uploadSchemes")
@ResponseBody
public List<String> uploadSchemes(MultipartFile[] files,Integer demandId,Integer userId){
List<String> list = new ArrayList<>();
String realPath = "E:\\idea\\project\\springboot\\witkey\\src\\main\\webapp\\uploads\\files";
for(int i=0;i<files.length;i++){
long size = files[i].getSize();
if(size>1000000){
list.add("该文件太大,无法上传");
return list;
}
//获取文件名称
String filename = files[i].getOriginalFilename();
filename = saveNewFileName(filename,demandId,userId);
list.add(filename);
File f = new File(realPath+File.separator+filename);
try {
files[i].transferTo(f);
} catch (IOException e) {
e.printStackTrace();
}
}
witkeyDemandSchemeService.insertDemandSchemes(demandId,userId,list);
return list;
}
private String saveNewFileName(String filename, Integer demandId, Integer userId) {
int index = filename.indexOf(".");
return filename.substring(0,index)+"_"+demandId+"_"+userId+filename.substring(index);
}
文件下载
前台就是写了个单击事件,把文件名称传到后台了,这个跳转路径别用ajax方式跳转.
这是后台的controller方法,接收方式必须是restful风格接收参数不然会下载不正确
import org.apache.tomcat.util.http.fileupload.IOUtils;
@RequestMapping("/downloadScheme/{filename:.+}")
public void downloadScheme(@PathVariable String filename, HttpServletResponse response){
//获取下载路径
String realPath = "E:\\idea\\project\\springboot\\witkey\\src\\main\\webapp\\uploads\\files";
File file = new File(realPath,filename);
//读取下载的文件
try(
InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();
){
response.setContentType("application/x-download");
filename= URLEncoder.encode(filename,"UTF-8");
response.addHeader("ContentDisposition","attachment;filename="+filename);
IOUtils.copy(in,out);
out.flush();
}catch (Exception e){
e.printStackTrace();
}
}
实现效果
断点续传版文件下载
//实现文件下载功能
@RequestMapping("/downloadScheme/{filename:.+}")
public void downloadFile(@PathVariable String filename, HttpServletResponse response, HttpServletRequest request){
File dir = new File("/var/www/tena-service/log");//获取文件路劲
File downloadFile = new File(dir, filename);//在指定目录下查找文件
try {
downloadFileRanges(downloadFile, request, response,filename);
} catch(ClientAbortException e){
System.out.println("连接被终止");
} catch (IOException e) {
e.printStackTrace();
}
}
private void downloadFileRanges(File downloadFile, HttpServletRequest request, HttpServletResponse response,String filename) throws IOException {
// 要下载的文件大小
long fileLength = downloadFile.length();
// 已下载的文件大小
long pastLength = 0;
// 是否快车下载,否则为迅雷或其他
boolean isFlashGet = true;
// 用于记录需要下载的结束字节数(迅雷或其他下载)
long lenEnd = 0;
// 用于记录客户端要求下载的数据范围字串
String rangeBytes = request.getHeader("Range");
//用于随机读取写入文件
RandomAccessFile raf = null;
OutputStream os = null;
OutputStream outPut = null;
byte b[] = new byte[1024];
// 如果客户端下载请求中包含了范围
if (null != rangeBytes)
{
// 返回码 206
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");
// 判断 Range 字串模式
if (rangeBytes.indexOf('-') == rangeBytes.length() - 1)
{
// 无结束字节数,为快车
isFlashGet = true;
rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
pastLength = Long.parseLong(rangeBytes.trim());
}
else
{
// 迅雷下载
isFlashGet = false;
String startBytes = rangeBytes.substring(0,
rangeBytes.indexOf('-'));
String endBytes = rangeBytes.substring(
rangeBytes.indexOf('-') + 1, rangeBytes.length());
// 已下载文件段
pastLength = Long.parseLong(startBytes.trim());
// 还需下载的文件字节数(从已下载文件段开始)
lenEnd = Long.parseLong(endBytes);
}
}
// 通知客户端允许断点续传,响应格式为:Accept-Ranges: bytes
response.setHeader("Accept-Ranges", "bytes");
// response.reset();
// 如果为第一次下载,则状态默认为 200,响应格式为: HTTP/1.1 200 ok
if (0 != pastLength)
{
// 内容范围字串
String contentRange = "";
// 响应格式
// Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]||[文件的总大小]
if (isFlashGet)
{
contentRange = new StringBuffer("bytes")
.append(new Long(pastLength).toString()).append("-")
.append(new Long(fileLength - 1).toString())
.append("/").append(new Long(fileLength).toString())
.toString();
}
else
{
contentRange = new StringBuffer(rangeBytes).append("/")
.append(new Long(fileLength).toString()).toString();
}
response.setHeader("Content-Range", contentRange);
}
String fileName = getDownloadChineseFileName(filename);
response.setHeader("Content-Disposition",
"attachment;filename=" + fileName + "");
// 响应的格式是:
response.setContentType("application/octet-stream");
response.addHeader("Content-Length", String.valueOf(fileLength));
try
{
os = response.getOutputStream();
outPut = new BufferedOutputStream(os);
raf = new RandomAccessFile(downloadFile, "r");
// 跳过已下载字节
raf.seek(pastLength);
if (isFlashGet)
{
// 快车等
int n = 0;
while ((n = raf.read(b, 0, 1024)) != -1)
{
outPut.write(b, 0, n);
}
}
else
{
// 迅雷等
while (raf.getFilePointer() < lenEnd)
{
outPut.write(raf.read());
}
}
outPut.flush();
}
catch (IOException e)
{
/**
* 在写数据的时候 对于 ClientAbortException 之类的异常
* 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时, 抛出这个异常,这个是正常的。 尤其是对于迅雷这种吸血的客户端软件。
* 明明已经有一个线程在读取 bytes=1275856879-1275877358,
* 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段, 直到有一个线程读取完毕,迅雷会 KILL
* 掉其他正在下载同一字节段的线程, 强行中止字节读出,造成服务器抛 ClientAbortException。
* 所以,我们忽略这种异常
*/
}
finally
{
if(outPut != null)
{
outPut.close();
}
if(raf != null)
{
raf.close();
}
}
}
private String getDownloadChineseFileName(String paramName)
{
String downloadChineseFileName = "";
try
{
downloadChineseFileName = new String(paramName.getBytes("GBK"),
"ISO8859-1");
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return downloadChineseFileName;
}