一、Ajax文件下载问题还原
1、前端代码
$(".delbtn").click(function () {
var _this = $(this);
// 文件id
var _this_file_id = _this.attr('name');
if (!_this_file_id || _this_file_id < 1) {
return false;
}
debugger
$.ajax({
url: '/pc/download/file',
data: {fileResourceId : _this_file_id},
type: "POST",
success: function() {
},
error: function () {
}
});
// window.location.href = _this_url;
});
2、后天代码
package com.antscity.fsm.site.controller.pc;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.OSSObject;
import com.antscity.common.util.StringUtil;
import com.antscity.fsm.base.model.wechat.LoginMember;
import com.antscity.kernel.base.model.common.AliyunOssProperties;
import com.antscity.kernel.base.model.common.FileResource;
import com.antscity.kernel.base.service.common.FileResourceService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Objects;
/**
* 文件下载服务
*/
@Controller
@RequestMapping("/pc/download/")
public class DownloadController {
@Autowired
private AliyunOssProperties ossProperties;
@Reference
private FileResourceService fileResourceService;
/**
* 文件下载
*
* @param fileResourceId
* @param loginMember
* @param response
*/
@RequestMapping(value = "file", method = RequestMethod.POST)
public void getFile(
Long fileResourceId, LoginMember loginMember,
HttpServletResponse response, HttpServletRequest request) throws IOException{
Long tenantId = loginMember.getTenantId();
FileResource fileResource = fileResourceService.findById(tenantId, fileResourceId);
if (Objects.isNull(fileResource)) return;
String url = fileResource.getUrl();
byte[] bytes;
try {
bytes = getOssFileByteArray(url);
} catch (OSSException | ClientException e) {
bytes = getUrlFileByteArray(url);
}
if (Objects.isNull(bytes)) return;
String fileName = getFileName(fileResource);
this.writeFileByteArray(response, bytes, fileName);
}
@RequestMapping(value = {"/fileByUrl"}, method = {RequestMethod.POST})
public void getFileByUrl(
String fileUrl, LoginMember loginMember,
HttpServletResponse response, HttpServletRequest request ) throws IOException {
byte[] bytes;
try {
bytes = getOssFileByteArray(fileUrl.split("&")[0]);
} catch (OSSException | ClientException e) {
bytes = getUrlFileByteArray(fileUrl.split("&")[0]);
}
if (Objects.isNull(bytes)) return;
this.writeFileByteArray(response, bytes, fileUrl.split("&")[1]);
}
/**
* 用OSSClient获取文件字节
*/
private byte[] getOssFileByteArray(String url) throws IOException {
OSSClient ossClient = new OSSClient(
ossProperties.getEndPoint(),
ossProperties.getAccessKeyId(),
ossProperties.getAccessKeySecret());
try {
int keyBeginIndex = url.lastIndexOf("/", url.lastIndexOf("/") - 1) + 1;
String key = url.substring(keyBeginIndex, url.length());
GetObjectRequest objectRequest = new GetObjectRequest(ossProperties.getBucketName(), key);
OSSObject ossObject = ossClient.getObject(objectRequest);
return this.getFileByteArray(ossObject.getObjectContent());
} finally {
ossClient.shutdown();
}
}
/**
* 用url获取文件字节
*/
private byte[] getUrlFileByteArray(String url) throws IOException {
URL urlObject = new URL(url);
return this.getFileByteArray(urlObject.openStream());
}
/**
* 获取到真正的下载文件名
*/
private String getFileName(FileResource fileResource) {
String nameOrUrl = StringUtil.isEmpty(fileResource.getFileName()) ?
fileResource.getUrl() :
fileResource.getFileName();
int nameBeginIndex = nameOrUrl.lastIndexOf("/") + 1;
return nameOrUrl.substring(nameBeginIndex, nameOrUrl.length());
}
/**
* 向response中写入文件字节
*/
private void writeFileByteArray(HttpServletResponse response, byte[] bytes, String fileName) throws IOException {
response.setContentType("application/octet-stream");
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
response.setContentLength(bytes.length);
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(bytes);
outputStream.flush();
}
/**
* 从输出流中读取到文件所有的字节
*/
private byte[] getFileByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int rc = 0;
while ((rc = inputStream.read(buff, 0, 1024)) > 0) {
swapStream.write(buff, 0, rc);
}
swapStream.close();
inputStream.close();
return swapStream.toByteArray();
}
}
3、结果展示
- PC没有展示下载信息,响应头正确,数据也正确的传到后台,当查看请求结果时(F12 NetWork),发现为乱码的字节串
二、解决
1、基本方法
不适用Ajax请求方式,使用get直接请求,后台代码不变
$(".delbtn").click(function () {
var _this = $(this);
var _this_file_id = _this.attr('name');
if (!_this_file_id || _this_file_id < 1) {
return false;
}
window.location.href = "/pc/download/file?fileResourceId=" + _this_file_id;
});
2、其他方式参考
三、总结原因
-
验证:ajax方式下载文件时无法触发浏览器打开保存文件对话框,也就无法将下载的文件保存到硬盘上!
-
原因:ajax方式请求的数据只能存放在javascipt内存空间,可以通过javascript访问,但是无法保存到硬盘,因为javascript不能直接和硬盘交互,否则将是一个安全问题。
网友评论