情景:我有多条数据,每条数据中都存了很多附件。现在,我想把这些附件全都下载下来,而我又不想一条数据一条数据的点开,然后一个附件一个附件的下载。请问我该怎么办?
数据库结构:
id |
title | enclosure |
1 | 美女照片合辑 | "[{\"type\":\"image\",\"url\":\"https://abc.xxx-xxx-xxxx.aliyuncs.com/ccc/upload/宇宙第一大美女.jpg\",\"name\":\"3c9293ec247a8632195f6d8b0a6e639b.jpg\",\"uid\":1519637358677, \"status\":\"success\",\"percentage\":100},
{\"type\":\"image\",\"url\":\"https://abc.xxx-xxx-xxxx.aliyuncs.com/ccc/upload/宇宙第二大美女.jpg\",\"name\":\"68-1612051H502.jpg\",\"uid\":1519637358678,\"status\":\"success\",\"percentage\":100}]" |
2 | 帅哥照片合辑 | "[{\"type\":\"image\",\"url\":\"https://abc.xxx-xxx-xxxx.aliyuncs.com/ccc/upload/10b04d56-4c95-48cf-ad14-51a8990ef36b.jpg\",\"name\":\"宇宙第一大帅哥.jpg\",\"uid\":1514340723425,\"status\":\"success\",\"percentage\":100}]" |
如上,我的数据库表结构是这样的,id是就是id,没什么好说的,title是我这条数据的名称,enclosure是我这条数据里带的附件,enclosure里存的是我把附件上传到文件服务器后附件在服务器的地址。第一条数据,它的标题叫“美女照片合辑”,它里面有两张美女的照片,在它对应的enclosure字段里我们也可以很清晰的看到有两条结构一样但是内容不一样的字符串(为了方便查看,已用空格隔开),如下:
而“帅哥照片合辑“里现在只有一张宇宙第一大帅哥的照片地址。
背景已交代完毕。现在确定功能实现思路:
(1) 通过id拿到对应数据enclosure字段的值,把附件地址全部罗列出来,放到一个集合A里;
(2)遍历集合A中中的附件地址,通过该地址拿到对应附件,把它转成的文件流,保存到一个压缩文件里;
(3)循环往复,直至所有附件都已下载完毕保存至压缩文件中,然后把压缩文件传到服务器中;
(4)拿到压缩包的地址,丢给前端,下载。
具体实现如下:
(按调用顺序controller→service书写)
/**
* 导出附件
* @return
*/
@ApiOperation("导出附件-照片合辑")
@PostMapping("export")
Output photoExport(Input input) {
//获取当前操作人的用户名,这是个人封装的方法,如果你没有的话可以不写,问题不大
String userName = this.getLoginUser().getUserName();
Output output = photoService.photoIdList(input,userName);
return output;
}
参数说明:
(1)首先参数Input只是一个自己封装的输入类而已,比如我有一个列表,我现在想下载标题名中含有“美女”两个字的所有数据的附件,那么我必定要先在一堆数据中把含有“美女”两个字的所有数据找出来,那“美女”这两个字必定是有前端传给后端的,而我这个input就只是一个用来接收前端传入的数据的一个容器而已。各人有个人的写法,各家有各家的框架,这个不必相同,掌握思想即可。
(2)this.getLoginUser().getUserName(); 这个是为了获取当前操作人的用户名,这是公司自己封装的方法,你们有类似的方法就用。没有就不用,问题不大。
第一步:根据前端传入的条件,找出所有符合条件的数据,把它们的id取出来放在一个数组中
/**
* 导出照片合辑附件的准备工作
* @param input
* @param username
* @return
*/
public Output photoIdList(Input input,String username) {
Output output=new Output();
StringBuilder sql=new StringBuilder("select * from photo ");
List<Photo> photoList = jdbcRepository.list(sql.toString(),input,Photo.class);
if(null!=photoList &&photoList .size()>0){
int[] ids=new int[photoList .size()];
for(int i=0;i<photoList .size();i++){
ids[i]=photoList.get(i).getId();
}
output.setResult(this.photoExport(ids,username));
}
return output;
}
/**
* 导出照片附件
* @return
*/
public Output photoExport(int[] ids, String userName) throws YaolingException {
logger.debug("开始导出附件");
//new一个新集合,用于存放所有id的所有附件地址集合
List<FileUrlBean> allList = new ArrayList<>();
//遍历数组,把每个id对应的数据查出来
for (int id:ids) {
//查出id对应的那条数据
Photo photo=photoRepository.findOne(id);
//new一个新集合,用于存放当前id的所有附件地址
List<FileUrlBean> fileUrlBeans = new ArrayList<>();
//如果这条数据的附件字段不为空
if(!ObjectUtils.isEmpty(photo.getEnclosure())){
//把附件字段的值取出来赋值给字符串url,此时的url包括所有附件的文件地址
String url = photo.getEnclosure();
//把地址中的“\\”替换成“”,其实就是把\换掉,记住,是\而不是\\,第一个\是转义
url = url.replace("\\","");
//截取url第1位到第url.length()-1位
url = url.substring(1,url.length()-1);
//截取的字符串转成json数组
JSONArray jsonArray = JSONArray.fromObject(url);
//数组转成fileUrlBean实体
fileUrlBeans = JSONArray.toList(jsonArray,FileUrlBean.class);
for (FileUrlBean fileUrlBean:fileUrlBeans){
//把该条id对应的title赋值给fileUrlBean的title字段
//后期用来标记各个图片都属于哪一条数据
fileUrlBean.setTitle(photo.getTitle);
}
allList.addAll(fileUrlBeans);
}
}
//调用下一个打包压缩的方法,并返回压缩包的云地址
String returnUrl = mutileFileToGzip(allList);
return new Output(returnUrl);
}
部分讲解如下:
更正:这里面FileUrlBean并不是我想要的字段的汇集,而是为url中图片地址多定义的一个地址。内容如下:
/**
* 将多个压缩文件为一个zip文件
* @param urlList 要压缩的文件url集合
*/
public String mutileFileToGzip(List<FileUrlBean> urlList) {
try {
byte[] buffer = new byte[1024];
// 创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//创建一个新的缓冲输出流,以将数据写入指定的底层输出流
BufferedOutputStream fos = new BufferedOutputStream(baos);
ZipOutputStream zos = new ZipOutputStream(fos);
UploadUtil uploadUtil = new UploadUtil();
for (int i = 0; i < urlList.size(); i++) {
if(urlList.get(i).getPercentage().equals("100")&&urlList.get(i).getStatus().equals("success")){
String spStr=urlList.get(i).getUrl();
InputStream fis= uploadUtil.downLoad2(spStr);//获取到线上文件的数据流
zos.putNextEntry(new ZipEntry(urlList.get(i).getTitle()+"_"+i+"_"+urlList.get(i).getName()));//压缩文件内的文件名称
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);//将文件读入压缩文件内
}
zos.closeEntry();
fis.close();
}
}
zos.close();
fos.flush();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String aa =uploadUtil.save("photo"+sdf.format(new Date()),new ByteArrayInputStream(baos.toByteArray()));
return aa;
} catch (Exception ioe) {
ioe.printStackTrace();
}
return null;
}
讲解如下:
/**
* 下载文件2
* @param url 文件路径
* @return
* @throws Exception
*/
public InputStream downLoad2(String url)throws Exception{
String prefix2 = "https://abc.xxx-xxx-xxxx.aliyuncs.com/";
String prefix1 = "https://def.xxx-xxx-xxxx.aliyuncs.com/";
String[] sp=null;
if(-1!=url.indexOf(prefix2)) {
sp = url.split(prefix2);
}else if(-1!=url.indexOf(prefix1)){
sp = url.split(prefix1);
}
if(null==sp){
throw new Exception("附件地址错误!");
}
String spStr=sp[1].toString();
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
OSSObject ossObject =null;
if(-1!=url.indexOf(prefix2)) {
ossObject=ossClient.getObject("abc",spStr);
}else if(-1!=url.indexOf(prefix1)){
ossObject=ossClient.getObject("def",spStr);
}
if(null==ossObject){
throw new Exception("附件获取失败!");
}
InputStream inputStream = ossObject.getObjectContent();
return inputStream;
}
(5)上传压缩包
public String save(String fileName,ByteArrayInputStream byteArrayInputStream) throws Exception {
// 1.创建OSSClient实例
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
// 2. 上传后文件名
StringBuilder name= new StringBuilder(fileName);
String key =name+".zip";
// 3.上传文件
ossClient.putObject(bucketName, key, byteArrayInputStream);
// 4 关闭client
ossClient.shutdown();
return prefix.concat(key);
}
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数