最近遇到一个功能要求从阿里云OSS服务器下载文件并批量导出,从网上找到的文章内容与现在阿里云文档操作手册有部分出入,导致拿取文件出问题,综合了大部分文章才完成功能,所以想记录一下以免下次用的时候忘记了
首先就是工具类中要连接OSS的Client
参考的文章如下:
前端批量下载请求报错处理
对返回的zip流以及blob格式的res处理
后端借鉴文章
阿里云OSS流式下载操作手册
插入maven依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.8.0</version>
</dependency>
这里我的配置信息是放在配置文件中的,看项目情况 也可以放到Nacos或者数据库里
oss.host: https://oss-cn-beijing.aliyuncs.com(阿里云域名)
oss.bucketName: tests-xxxxxxx-----(oss创建的服务名)
oss.endpoint: oss-cn-beijing.aliyuncs.com(阿里云域名)
oss.accessKeyId: LTAxxxxxxxxxxxxxxxxx(这里的ID和Secret需要在阿里云的RAM)
oss.accessKeySecret: 1QInP3xxxxxxxxxxxxxxxxxxx
@Data
@Component
public class OssUtil {
//读取OSS服务配置信息
private String host = AppConfig.getProperty("oss.host");
private String endpoint = AppConfig.getProperty("oss.endpoint");
private String accessKeyId = AppConfig.getProperty("oss.accessKeyId");
private String accessKeySecret = AppConfig.getProperty("oss.accessKeySecret");
private String bucketName = AppConfig.getProperty("oss.bucketName");
/**
* 上传文件
* @param file
* @return
*/
public HashMap<String, Object> upload(MultipartFile file,String path) {
if (file.isEmpty()) {
throw new ServiceException("上传文件不能为空");
}
if (!checkFileSize(file.getSize(), BaseConstant.LEN, BaseConstant.UNIT_M)) {
throw new ServiceException("上传文件大小不能超过10M");
}
HashMap<String, Object> resultMap = new HashMap<>();
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上传文件流。
// 保存路径,注意:这里不要以 / 或 \ 开头
path += "/"+new SimpleDateFormat("yyyyMMdd").format(new Date());
String originalFilename = file.getOriginalFilename();
String uuidName = UUID.randomUUID().toString().replace("-", "");
String fileName = path + "/" + uuidName + originalFilename.substring(originalFilename.lastIndexOf("."));
resultMap.put("path",path);
resultMap.put("fileName",uuidName+originalFilename.substring(originalFilename.lastIndexOf(".")));
try {
ossClient.putObject(bucketName, fileName, file.getInputStream());
} catch (IOException e) {
throw new ServiceException("上传失败" + e.getMessage());
}
// 关闭OSSClient。
ossClient.shutdown();
return resultMap;
}
/**
* 删除文件
* @param url 示例:'download/file.xsl' oss服务器文件路径以及文件名
*/
public void remove(String url) {
try {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 判断当前文件url 是否存在
boolean exist = ossClient.doesObjectExist(bucketName, url);
if (!exist) {
System.out.println("文件不存在");
} else {
// 删除文件。
ossClient.deleteObject(bucketName, url);
// 关闭OSSClient。
ossClient.shutdown();
}
} catch (Exception e) {
System.out.println(e);
}
}
/**
* 下载文件,此处主要是给Controller层提供一个OSS的Object类
* objectName示例:download/file.xsl,()这里是oss文件列表中 文件路径和文件名
*/
public OSSObject downloadOssFile(OutputStream os, String objectName) throws IOException {
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
OSSObject ossObject = ossClient.getObject(bucketName, objectName);
return ossObject;
}
/**
* 校验文件
* @param len
* @param size
* @param unit
* @return
*/
public boolean checkFileSize(Long len, int size, String unit) {
double fileSize = 0;
if (BaseConstant.UNIT_B.equals(unit.toUpperCase())) {
fileSize = (double) len;
} else if (BaseConstant.UNIT_K.equals(unit.toUpperCase())) {
fileSize = (double) len / 1024;
} else if (BaseConstant.UNIT_M.equals(unit.toUpperCase())) {
fileSize = (double) len / 1048576;
} else if (BaseConstant.UNIT_G.equals(unit.toUpperCase())) {
fileSize = (double) len / 1073741824;
}
if (fileSize > size) {
return false;
}
return true;
}
}
文件常量类,在工具类中有使用到,初步了解应该是用来限制文件的
public class BaseConstant {
public static final Integer NO = 0;
public static final Integer YES = 1;
public static final int LEN = 10;
public static final String UNIT_M = "M";
public static final String UNIT_B = "B";
public static final String UNIT_K = "K";
public static final String UNIT_G = "G";
}
以上便是工具类的创建,下面是前后端上传代码
前端代码
<q-dialog ref="addFileModal" width="30%" height="300" title="新增附件" :showFooter="true">
<el-form ref="addfileForm" :model="addfileForm" label-width="90px" class="searForm" size="mini">
<el-form-item label="阶段名称:">
<el-select v-model="addfileForm.jdid" filterable placeholder="请选择" style="width: 100%;">
<el-option v-for="item in jdNameList" :key="item.JDID" :label="item.JDMC" :value="item.JDID" />
el-select>
el-form-item>
<el-form-item label="上传附件:">
<el-upload
class="upload-demo"
action
:http-request="importMxData"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="3"
:on-exceed="handleExceed"
:file-list="fileList">
<el-button size="small" type="primary">上传附件el-button>
el-upload>
el-form-item>
<el-form-item label="备注:">
<el-input type="textarea" v-model="addfileForm.bz" placeholder="请输入备注">el-input>
el-form-item>
el-form>
<div slot="footer">
<q-button type="primary" @click="saveFileJd()">确定q-button>
<q-button @click="addFileClose">取消q-button>
div>
q-dialog>
下载的HTMl代码就不贴了,只要一个button能触发此方法就可以了
import request from '@/utils/request'
//下载请求
downRequest: function (url, data) {
return request({
url: contextPath + url,
method: 'get',
params: data,
responseType: 'blob',//设置返回类型
headers: {'Content-Type': 'application/json; application/octet-stream'}//设置请求头
})
}
import http from '../index'
export function downAllOssFile(params) {
return http.downRequest('xxxxxxxxxxxx/xxxxxxxxxxxx',params)
}
上传需要将文件转换为FormData格式
data(){
return{
FileForm:''
}
}
//处理附件
importMxData(item){
let FormDatas = new FormData()
FormDatas.append('file',item.file);
FormDatas.append('time',1);
FormDatas.append('epath','sanss');
this.FileForm = FormDatas
},
//上传附件
saveFileJd(){
//追加参数
this.FileForm.append('jdid',this.addfileForm.jdid);
//调用方法,这里前端大哥封装的调接口方法直接传FileForm数组
insetStageFile(this.FileForm).then(res => {
if (res.code === 0) {
//关闭弹窗
this.addFileClose()
} else {
this.$message.error(res.msg)
this.addFileClose()
}
}).catch(error => {
this.addFileClose()
})
},
//批量下载-----------------------------------
bulkDownload(){
downAllOssFile({
filename:'fujian/fujian1.xls,fujian/fujian2.xls'
}).then(res => {
const content = res
const blob = new Blob([content])
const fileName = 'LOG.zip'
if ('download' in document.createElement('a')) { // 非IE下载
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href) // 释放URL 对象
document.body.removeChild(elink)
} else { // IE10+下载
navigator.msSaveBlob(blob, fileName)
}
}).catch(error => {
})
},
控制层代码
/**
* 上传文件到OSS
* @return
*/
@PostMapping("/upload")
public R upload(@RequestParam("file") MultipartFile file) {
//文件夹名称
String test = "muban";
return new R(ossUtil.upload(file,test));
}
/**
* 项目资料列表 点击删除附件
* @return
*/
@GetMapping("/deleteFile")
@ResponseBody
public R deleteFile(@RequestParam("epath") String epath,@RequestParam("ename") String ename) {
//前端传过来文件路径以及文件名,路径以及文件名要做加密解密,这里只做示例展示
ossUtil.remove(epath +"/"+ ename);
return R.success();
}
/***
* 批量下载(zip)
* @param objectName
* @param response
* @throws IOException
* objectName为前端传过来的多个文件路径字符串,用逗号隔开('filePath/file.xsl','filePath/file1.xsl','filePath/file2.xsl')
*/
@RequestMapping("downAllOssFile")
@ResponseBody
public void downAllOssFile(@RequestParam("objectName") String objectName,HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
String momentFileName = "全部资源下载.zip";
// 创建临时文件
File zipFile = File.createTempFile("批量下载", ".zip");
FileOutputStream f = new FileOutputStream(zipFile);
CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
// 用于将数据压缩成Zip文件格式
ZipOutputStream zos = new ZipOutputStream(csum);
String[] files = objectName.split(",");
for (String string : files) {
String fName = string.trim();
String eachFileName = fName.substring(fName.lastIndexOf("/")+1);
String tmp = fName.substring(0,fName.lastIndexOf("/"));
tmp = tmp.substring(tmp.lastIndexOf("/")+1);
String fileName = tmp + "/" + eachFileName;
OSSObject ossObject = ossUtil.downloadOssFile(response.getOutputStream(), fileName);
InputStream inputStream = ossObject.getObjectContent();
zos.putNextEntry(new ZipEntry(eachFileName));
int bytesRead;
// 向压缩文件中输出数据
while((bytesRead = inputStream.read())!=-1){
zos.write(bytesRead);
}
inputStream.close();
zos.closeEntry();
}
zos.close();
String header = request.getHeader("User-Agent").toUpperCase();
if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
momentFileName = URLEncoder.encode(momentFileName, "utf-8");
momentFileName = momentFileName.replace("+", "%20"); //IE下载文件名空格变+号问题
} else {
momentFileName = new String(momentFileName.getBytes(), "ISO8859-1");
}
response.reset();
response.setContentType("text/plain");
response.setContentType("application/octet-stream; charset=utf-8");
response.setHeader("Location", momentFileName);
response.setHeader("Cache-Control", "max-age=0");
response.setHeader("Content-Disposition", "attachment; filename=" + momentFileName);
FileInputStream fis = new FileInputStream(zipFile);
BufferedInputStream buff = new BufferedInputStream(fis);
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
byte[] car = new byte[1024];
int l=0;
while (l < zipFile.length()) {
int j = buff.read(car, 0, 1024);
l += j;
out.write(car, 0, j);
}
// 关闭流
fis.close();
buff.close();
out.close();
// 删除临时文件
zipFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}