Spring Boot 2.X 文件上传与下载

前端部分

单文件上传

GET的参数会以明文的形式附带在url后面,因此只能用于传递少量且不考虑安全的数据。
如果表单中含有二进制数据,那么一定只能用POST提交,而且表单的 enctype 属性必须设置为 multipart/form-data。action 声明了表单提交的地址。

多文件上传

多文件上传和单文件,在前端的区别只是在input标签中是否加上 multiple 标记而已。

文件下载



文件下载用到了 prompt 请求输入框,返回值是输入的内容。如果单击取消,会返回 null。

function downloadPromptUrl() {
	const value = prompt("请输入文件名", "test.jpg");
	if(value !== null) downloadUrl(value);
}
function downloadPromptBlob() {
	const value = prompt("请输入文件名", "test.jpg");
	if(value !== null) downloadBlob(value);
}

下载文件的两种方法:

  1. 是用ajax拿到二进制返回值放入a标签中,然后点开a标签(注意,a标签一定要加到 html 中点击事件才会有效,可以给 a 标签样式的 display 设置为 none,让它不显示 )。但是由于这种方式,不同浏览器的API不同,所以要对 IE、Chrome、Firefox 做不同的处理,比较麻烦所以不推荐使用。
    Spring Boot 2.X 文件上传与下载_第1张图片
  2. 是用 window.location.href 或 window.open 打开下载连接
    Spring Boot 2.X 文件上传与下载_第2张图片

AJAX 的 XMLHttpRequest.status 属性

onreadystatechange 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。

readyState 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。

  • 0: 请求未初始化
  • 1: 服务器连接已建立
  • 2: 请求已接收
  • 3: 请求处理中
  • 4: 请求已完成,且响应已就绪

status

  • 200: “OK”
  • 404: 未找到页面
function getBrowser() {
    var ua = window.navigator.userAgent;
    var isIE = !!window.ActiveXObject || "ActiveXObject" in window;
    var isFirefox = ua.indexOf("Firefox") != -1;
    var isOpera = window.opr != undefined;
    var isChrome = ua.indexOf("Chrome") && window.chrome;
    var isSafari = ua.indexOf("Safari") != -1 && ua.indexOf("Version") != -1;
    if (isIE) {
        return "IE";
    } else if (isFirefox) {
        return "Firefox";
    } else if (isOpera) {
        return "Opera";
    } else if (isChrome) {
        return "Chrome";
    } else if (isSafari) {
        return "Safari";
    } else {
        return "Unkown";
    }
}

function downloadUrl(downloadName) {
	const url = "http://127.0.0.1:8080/download/" + downloadName;
//	window.location.href = url;
	window.open(url)
}
function downloadBlob(downloadName) {
	const url = "http://127.0.0.1:8080/download/" + downloadName;
	const xhr = new XMLHttpRequest();
	xhr.responseType = "blob";
	xhr.open('GET', url);
	xhr.onreadystatechange = function(e) {
		console.log(this.readyState + ", " + this.status);
		if (this.readyState == 4 && this.status == 200) {
 			const type = xhr.getResponseHeader("Content-Type");
 			console.log(type)
	    	const name = xhr.getResponseHeader("Content-Disposition");
	    	console.log(name);
	    	const filename = name.split(";")[1].substring(9).replace(/^[\'\"]+|[\'\"]+$/g,"");
	    	console.log(filename);
	    	/*
            const blob = new Blob([xhr.response], {type: 'image/jpeg'});
            const objectURL = URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = objectURL;
            link.download = filename;
            link.innerText = "test.jpg";
            //link.style.display="none";
            //只有添加到html中,点击事件才有效
            document.body.appendChild(link);
            link.click();
            */
            
            var browser = getBrowser();
            console.log(browser)
            if (browser == "Chrome") {
                var link = document.createElement('a');
                var file = new Blob([xhr.response], { type: type });
                link.href = window.URL.createObjectURL(file);
                link.download = filename;
                link.click();
                window.URL.revokeObjectURL(link.href);
            } else if (browser == "Firefox") {
                var file = new File([xhr.response], filename, { type: type });
                var url = URL.createObjectURL(file);
                parent.location.href = url;
                window.URL.revokeObjectURL(url);
            }
	    }
	}
	xhr.send(null);
}

后端部分

预置常量部分

对于 java.io.tmpdir 的路径

  • Windows C:\Users\用户名\AppData\Local\Temp\
  • Linux /tmp
    由于这个文件夹中存放了所有应用的零时文件,因此建议缓存的时候,在该目录下新建本项目自己的文件夹,用来存放该项目的零时文件
private static final String TMP_DIR = System.getProperty("java.io.tmpdir") + "Diffpatch" + File.separator;
private static final String FILE_DIR = TMP_DIR + "file" + File.separator;
private static final String CACHE_DIR = TMP_DIR + "cache" + File.separator;
private static final int BUFF_SIZE = 1024;

private ResultModel newResult(boolean success, String msg) {
	return new ResultModel(success, msg, TMP_DIR);
}

接收单文件

当前端不选择任何文件直接点 “提交” 的时候,会返回 “文件为空”

@PostMapping(value="/upload")
public ResultModel upload(@RequestParam("file") MultipartFile file) {
	if(file.isEmpty()) {
		return newResult(false, "文件为空");
	}
	//确保存储目录存在
	File dir = new File(FILE_DIR);
	if(!dir.exists()) dir.mkdirs();
	//文件名
	String fileName = file.getOriginalFilename();
	String path = FILE_DIR + fileName;
	try {
		File dstFile = new File(path);
		file.transferTo(dstFile);
		return newResult(true, "上传成功");
	} catch (IllegalStateException e) {
		e.printStackTrace();
		return newResult(false, e.toString());
	} catch (IOException e) {
		e.printStackTrace();
		return newResult(false, e.toString());
	}
}

接收多文件

即使前端不选择任何文件,也是可以取得一个长度为1的 MultipartFile 列表,只是它的 isEmpty 属性为真。

当然,在我的前一篇文章中,试过了,用 MultipartFile[] 也是可以的。

@PostMapping(value="/uploadbatch")
public ResultModel uploadBatch(HttpServletRequest request) {
	List files = ((MultipartHttpServletRequest) request).getFiles("file");
	MultipartFile file = null;
	BufferedOutputStream stream = null;
	byte[] buffer = new byte[BUFF_SIZE];
	int len;
	for(int i=0; i

文件下载

Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。

Content-Type 一般是附带文件编码的,常用的如下

  • response.setContentType(“text/html; charset=utf-8”);
  • response.setContentType(“text/plain; charset=utf-8”);

HttpServletRequest 和 HttpServletResponse 分别是原始的请求和下载,可以放在函数中,也可以自动绑定到类的属性里。

@GetMapping(value="/download/{name}")
public void download(@PathVariable String name, HttpServletResponse response) throws IOException {
	String path = FILE_DIR + name;
	File file = new File(path);
	if(file.exists()) {
		//设置强制下载不打卡
		response.setContentType("application/force-download");
		response.addHeader("Content-Disposition", "attachment;fileName=\"" + name + "\"");
		response.setContentLength((int) file.length());  
		byte[] buffer = new byte[1024];
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		try {
			fis = new FileInputStream(file);
			bis = new BufferedInputStream(fis);
			OutputStream os = response.getOutputStream();
			int len;
			while((len = bis.read(buffer)) != -1) {
				os.write(buffer, 0, len);
			}
			response.flushBuffer();
		} catch (IOException e) {
			e.printStackTrace();
			response.sendError(HttpServletResponse.SC_NOT_FOUND, e.toString());
		} finally {
			try {
				if(bis != null) bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(fis != null) fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	} else {
		response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
	}
}

你可能感兴趣的:(——,Spring,Boot,2.X)