文件上传
SpringBoot的文件上传相对比较简单,文件信息都包含在MultipartFile对象中,只要从中获取文件信息即可
不煽情,直接上代码吧,这个部分出门右拐“百度一下”一大堆
/**
* 单文件上传
*
* @param name 携带的其他文本表单(可以省略)
* @param file 文件内容
* @return
*/
@RequestMapping(value = "/upload_single", method = RequestMethod.POST)
public @ResponseBody String handleFileUpload(@RequestParam("name") String name,@RequestParam("file") MultipartFile file) {if (!file.isEmpty()) {BufferedOutputStream out = null;try {byte[] bytes = file.getBytes();out = new BufferedOutputStream(new FileOutputStream(new File(BASE_PATH + "\\" + name + "-" + file.getOriginalFilename())));out.write(bytes);return "You successfully uploaded " + name + " into " + name + "-uploaded !";} catch (Exception e) {return "You failed to upload " + name + " => " + e.getMessage();} finally {if (null != out) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}} else {return "You failed to upload " + name + " because the file was empty.";}
}
测试:
<p>单文件上传</p>
<form method="POST" enctype="multipart/form-data" action="/upload_single"> File to upload: <input type="file" name="file"/><br /> Name: <input type="text" name="name"/><input type="submit" value="单文件上传"/>
</form>
多文件上传
多文件上传需要接收解析出MultipartHttpServletRequest对象中包含的文件信息
/**
* 多文件上传,主要是使用了MultipartHttpServletRequest和MultipartFile
*
* @param name 携带的其他文本表单(可以省略)
* @param request
* @return
*/
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String handleFileUpload(HttpServletRequest request,@RequestParam("name") String name) {List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");System.out.println("name==>"+name);MultipartFile file = null;BufferedOutputStream out = null;for (int i = 0; i < files.size(); ++i) {file = files.get(i);if (!file.isEmpty()) {try {byte[] bytes = file.getBytes();out = new BufferedOutputStream(new FileOutputStream(new File(BASE_PATH + "\\" + file.getOriginalFilename())));out.write(bytes);} catch (Exception e) {return "failed to upload " + i + " => " + e.getMessage();} finally {if (null != out) {try {out.close();} catch (IOException e) {e.printStackTrace();}out = null;}}} else {return "failed to upload " + i + " because the file was empty.";}}return "upload successful";
}
测试:
<p>多文件上传</p>
<form method="POST" enctype="multipart/form-data"action="/upload">Name: <input type="text" name="name"/><p>文件1:<input type="file" name="file" /></p><p>文件2:<input type="file" name="file" /></p><p><input type="submit" value="多文件上传" /></p>
</form>
文件下载(断点续传)
相对于上传在文件下载的处理上,需要自己处理HttpServletResponse中流的返回,这里直接上一个有断点续传的代码:
@RequestMapping(value = "/download", method = RequestMethod.GET)
public void getDownload(String name, HttpServletRequest request, HttpServletResponse response) {// Get your file stream from wherever.String fullPath = "D:\\upload_test\\" + name;File downloadFile = new File(fullPath);ServletContext context = request.getServletContext();// get MIME type of the fileString mimeType = context.getMimeType(fullPath);if (mimeType == null) {// set to binary type if MIME mapping not foundmimeType = "application/octet-stream";}// set content attributes for the responseresponse.setContentType(mimeType);// response.setContentLength((int) downloadFile.length());// set headers for the responseString headerKey = "Content-Disposition";String headerValue = String.format("attachment; filename=\"%s\"", downloadFile.getName());response.setHeader(headerKey, headerValue);// 解析断点续传相关信息response.setHeader("Accept-Ranges", "bytes");long downloadSize = downloadFile.length();long fromPos = 0, toPos = 0;if (request.getHeader("Range") == null) {response.setHeader("Content-Length", downloadSize + "");} else {// 若客户端传来Range,说明之前下载了一部分,设置206状态(SC_PARTIAL_CONTENT)response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);String range = request.getHeader("Range");String bytes = range.replaceAll("bytes=", "");String[] ary = bytes.split("-");fromPos = Long.parseLong(ary[0]);if (ary.length == 2) {toPos = Long.parseLong(ary[1]);}int size;if (toPos > fromPos) {size = (int) (toPos - fromPos);} else {size = (int) (downloadSize - fromPos);}response.setHeader("Content-Length", size + "");downloadSize = size;}// Copy the stream to the response's output stream.RandomAccessFile in = null;OutputStream out = null;try {in = new RandomAccessFile(downloadFile, "rw");// 设置下载起始位置if (fromPos > 0) {in.seek(fromPos);}// 缓冲区大小int bufLen = (int) (downloadSize < 2048 ? downloadSize : 2048);byte[] buffer = new byte[bufLen];int num;int count = 0; // 当前写到客户端的大小out = response.getOutputStream();while ((num = in.read(buffer)) != -1) {out.write(buffer, 0, num);count += num;//处理最后一段,计算不满缓冲区的大小if (downloadSize - count < bufLen) {bufLen = (int) (downloadSize-count);if(bufLen==0){break;}buffer = new byte[bufLen];}}response.flushBuffer();} catch (IOException e) {e.printStackTrace();} finally {if (null != out) {try {out.close();} catch (IOException e) {e.printStackTrace();}}if (null != in) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}
}
该部分本人直接在Android客户端上测试:
下载代码:
public static boolean downLoadFile(String url, int from, int to,String savePath) {try {URL link = new URL(url);HttpURLConnection conn = (HttpURLConnection) link.openConnection();// 设置断点续传的开始位置if (to != 0) {conn.setRequestProperty("Range", "bytes=" + from + "-" + to);}else{conn.setRequestProperty("Range", "bytes=" + from + "-");}Log.e("m_tag", "code:" + conn.getResponseCode());if (conn.getResponseCode() == 206) {RandomAccessFile file = new RandomAccessFile(savePath, "rw");file.seek(from);InputStream in = conn.getInputStream();byte[] buffer = new byte[1024];int num;while ((num = in.read(buffer)) > 0) {file.write(buffer, 0, num);}file.close();in.close();return true;}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return false;
}
Activity中运用:
class MyTask extends AsyncTask<Object, Integer, Object> {@Overrideprotected Object doInBackground(Object... params) {String uri = (String) params[0];int from = (Integer) params[1];int to = (Integer) params[2];String savePath = (String) params[3];return HttpUtil.downLoadFile(uri, from, to, savePath);}@Overrideprotected void onPostExecute(Object result) {// TODO Auto-generated method stubsuper.onPostExecute(result);tvRes.setText(result.toString());}}//这里用两个按钮点击模拟分段,第一个按钮下载0-50B,第二个从50B下载到末尾
...省略其他部分...
case R.id.btn_part_01:new MyTask().execute("http://192.168.2.183:8080/download?name=11.jpg",0, 50, "/mnt/sdcard/pic_123.jpg");
break;
case R.id.btn_part_02:new MyTask().execute("http://192.168.2.183:8080/download?name=11.jpg",50, 0, "/mnt/sdcard/pic_123.jpg");
break;
...省略其他部分...
[附]
文件上传时相对路径配置
以上的上传文件可以保存到服务器端的D:\upload_test但是对于这个路径,客户端如果想要使用一个相对路径来访问的话,需要做以下配置:
1、在application.properties中声明一个变量描述该地址
#图片上传存储路径
imagesPath=file:/D:/upload_test/
2、定义一个配置文件可以将该绝对路径映射到一个/images的相对路径上
package com.example.demo.config;import org.apache.log4j.spi.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configuration
public class FileConfig extends WebMvcConfigurerAdapter {@Value("${imagesPath}") private String mImagesPath;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {if (mImagesPath.equals("") || mImagesPath.equals("${imagesPath}")) {String imagesPath = FileConfig.class.getClassLoader().getResource("").getPath();if (imagesPath.indexOf(".jar") > 0) {imagesPath = imagesPath.substring(0, imagesPath.indexOf(".jar"));} else if (imagesPath.indexOf("classes") > 0) {imagesPath = "file:" + imagesPath.substring(0, imagesPath.indexOf("classes"));}imagesPath = imagesPath.substring(0, imagesPath.lastIndexOf("/")) + "/images/";mImagesPath = imagesPath;}System.out.println("imagesPath=" + mImagesPath);registry.addResourceHandler("/images/**").addResourceLocations(mImagesPath);super.addResourceHandlers(registry);}
}
这样之后对于服务器上D:\upload_test\1.jpg这个图片可以在客户端直接使用http://127.0.0.1:8080/images/1.jpg这个路径就可以访问了