知识点:
- freemarker
- GridFS
- springBoot + vue 实现文件上传下载
freemarker
简单介绍
freemarker是一种类似于jsp的免费的java模板引擎,他可以基于模板和数据生成数据(可以是html,email,world,excel等等)
类似于freemarker的模板引擎还有jsp,Thymeleaf,Velocity等
数据模型
数据模型在java中可以是基本类型,也可以是List,Map,Pojo等复杂类型
模板
通过数据模型和模板一起生成最终的文件
下面提供一个官方的样例:
数据模型:
模板:
输出文件:
freemarker在springBoot环境中的配置文件
spring:
freemarker:
cache: false #关闭模板缓存
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查
核心指令
-
${name} 从数据模型中获取name属性,name可以是pojo的属性,也可以是map中的一个key
Hello World! Hello ${name}! -
List 指令 语法 <#list lst as l >
_index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始
<table> <tr> <td>序号</td> <td>姓名</td> <td>年龄</td> <td>钱包</td> </tr> <#list stus as stu> <tr> <#--stu_index 为内置属性,代表当前循环下标 --> <td>${stu_index + 1}</td> <td>${stu.name}</td> <td>${stu.age}</td> <td>${stu.mondy}</td> </tr> </#list> </table> ```
-
遍历map中的数据
freemarker没有提供直接遍历map的指令,但是可以通过list指令遍历map.keys来实现对map的遍历
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#list stuMap?keys as k>
<tr>
<td>${k_index + 1}</td>
<td>${stuMap[k].name}</td>
<td>${stuMap[k].age}</td>
<td >${stuMap[k].mondy}</td>
</tr>
</#list>
</table>
- if指令 <#if >
<table>
<tr>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#list stus as stu>
<tr>
<td
<#if stu.name =='小明'>style="background:red;"</#if>
>${stu.name}</td>
<td>${stu.age}</td>
<td >${stu.mondy}</td>
</tr>
</#list>
</table>
-
判断是否为空 <#if stus??>
<#if stus??> <#list stus as stu> ...... </#list> </#if>
-
空值处理 ,当数据为空的时候给与默认值
缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。
例:${name!’’}表示如果name为空显示空字符串。
如果是嵌套对象则建议使用()括起来。
例: ${(stu.bestFriend.name)!’’}表示,如果stu或bestFriend或name为空默认显示空字符串。
-
内建函数
内建函数是freemarker提供的一些函数 语法格式为 变量 + ? + 函数名称
-
获取集合的大小 ${集合名?size}
-
日期格式化
- 显示年月日: ${today?date}
- 显示时分秒:${today?time}
- 显示日期+时间:${today?datetime}
- 自定义格式化: ${today?string(“yyyy年MM月”)}
-
内建函数c
map.put(“point”, 102920122);
point是数字型,使用${point}会显示这个数字的值,不并每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出
${point?c}没加c之前的输出 102,920,122
加了之后的输出 102920122
-
将json字符串转成对象(定义一个常量,assign标签)
<#assign text="{‘bank’:‘工商银行’,‘account’:‘10101920201920212’}" />
<#assign data=text?eval />
开户行: d a t a . b a n k 账 号 : {data.bank} 账号: data.bank账号:{data.account}
脱离spring单独使用freemarker
基于模板文件生成文件
//基于模板生成静态化文件 @Test public void testGenerateHtml() throws IOException, TemplateException { //创建配置类 Configuration configuration=new Configuration(Configuration.getVersion()); //设置模板路径 String classpath = this.getClass().getResource("/").getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/")); //设置字符集 configuration.setDefaultEncoding("utf‐8"); //加载模板 Template template = configuration.getTemplate("test1.ftl"); //数据模型 Map<String,Object> map = new HashMap<>(); map.put("name" , "黑马程序员"); //静态化 String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); //静态化内容 System.out.println(content); InputStream inputStream = IOUtils.toInputStream(content); //输出文件 FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html")); int copy = IOUtils.copy(inputStream, fileOutputStream); }
基于字符串生成
//基于模板字符串生成静态化文件 @Test public void testGenerateHtmlByString() throws IOException, TemplateException { //创建配置类 Configuration configuration=new Configuration(Configuration.getVersion()); //模板内容,这里测试时使用简单的字符串作为模板 String templateString= "" + "<html>\n" + " <head></head>\n" + " <body>\n" + " 名称:${name}\n" + " </body>\n" + "</html>"; //模板加载器 StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); stringTemplateLoader.putTemplate("template" ,templateString); configuration.setTemplateLoader(stringTemplateLoader); //得到模板 Template template = configuration.getTemplate("template" , "utf‐8"); //数据模型 Map<String,Object> map = new HashMap<>(); map.put("name" , "黑马程序员"); //静态化 String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); //静态化内容 System.out.println(content); InputStream inputStream = IOUtils.toInputStream(content); //输出文件 FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/test1.html")); IOUtils.copy(inputStream, fileOutputStream); }
-
GridFS
GridFS简介
GridFS是MongoDB提供的用于持久化存储文件的模块,CMS使用MongoDB存储数据,使用GridFS可以快速集成开发。
它的工作原理是:
在GridFS存储文件是将文件分块存储,文件会按照256KB的大小分割成多个块进行存储,GridFS使用两个集合(collection)存储文件,一个集合是chunks, 用于存储文件的二进制数据;一个集合是files,用于存储文件的元数据信息(文件名称、块大小、上传时间等信息)。从GridFS中读取文件要对文件的各各块进行组装、合并。
详细参考:https://docs.mongodb.com/manual/core/gridfs/
存文件
// 需要使用spring中的gridFsTemplate
@Test
public void testGridFs() throws FileNotFoundException {
//要存储的文件
File file = new File("d:/index_banner.html");
//定义输入流
FileInputStream inputStram = new FileInputStream(file);
//向GridFS存储文件
ObjectId objectId = = gridFsTemplate.store(inputStram,
"轮播图测试文件01", "");
//得到文件ID
String fileId = objectId.toString();
System.out.println(file);
}
取文件
取文件比存文件麻烦一点,首先需要配置GridFSBucket,这个对象的作用是用于打开下载流
配置类
@Configuration
public class MongoConfig {
@Value("${spring.data.mongodb.database}")
String db;
@Bean
public GridFSBucket getGridFSBucket(MongoClient mongoClient){
MongoDatabase database = mongoClient.getDatabase(db);
GridFSBucket bucket = GridFSBuckets.create(database);
return bucket;
}
}
测试类
@SpringBootTest
@RunWith(SpringRunner.class)
public class GridFsTest {
@Autowired
GridFsTemplate gridFsTemplate;
@Autowired
GridFSBucket gridFSBucket;
@Test
public void queryFile() throws IOException {
String fileId = "5b9c54e264c614237c271a99";
//根据id查询文件
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
//打开下载流对象
GridFSDownloadStream gridFSDownloadStream =
gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建gridFsResource,用于获取流对象
GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
//获取流中的数据
String s = IOUtils.toString(gridFsResource.getInputStream(),"UTF‐8");
System.out.println(s);
}
}
springBoot + vue 实现文件上传下载
后台代码
@RequestMapping("/template")
@Controller
public class CmsTemplateFileController extends BaseController implements CmsTemplateFileControllerApi {
@Autowired
private CmsTemplateService cmsTemplateService;
@Override
@PostMapping("/uploadTemplate/{id}")
@ResponseBody
public ResponseResult uploadTemplate(@PathVariable("id") String templateId, @RequestParam("file") MultipartFile file) {
// 调用上一个知识点的内容将文档信息存储到GridFS中
return cmsTemplateService.uploadTemplate(templateId, file);
}
@Override
@GetMapping("/downTemplate/{id}")
public void downTemplate(@PathVariable("id") String templateId,@RequestParam("fileName") String fileName) {
QueryResult<String> result = cmsTemplateService.queryTemplate(templateId);
List<String> list = result.getList();
if (list.size() > 0) {
String templateFileContent = list.get(0);
try {
response.setHeader("content-type", "application/octet-stream");
// 设置charset=GBK 保证下载的文件名显示不乱码
response.setContentType("application/octet-stream;charset=GBK");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("utf-8"), "iso-8859-1"));
IOUtils.write(templateFileContent,response.getOutputStream(),"utf-8");
response.getOutputStream().flush();
} catch (IOException e) {
ExceptionCast.cast(CmsCode.CMS_TEMPLATE_FILE_DOWN_FAILD);
}
}
}
}
前端代码
<!-- template部分关键代码 -->
<el-table-column label="模板" width="160">
<template slot-scope="page">
<el-upload
:limit="1"
ref="upload"
accept=".ftl"
:action="uploadUrl + page.row.templateId"
:on-success="uploadSuccess"
style="float:left;" :show-file-list="false" >
<el-button
size="small" type="text">上传
</el-button>
</el-upload>
<el-button v-if="page.row.templateFileId" size="small"
type="text" style="float:left;margin-left:10px;"
@click="downTemplate(page.row)">下载
</el-button>
</template>
</el-table-column>
//script 部分关键代码
// 加载文件信息到右侧文本框内
changeRow: function (newRow, oldRow) {
if (newRow) {
cmsApi.template_queryFile(newRow.templateId).then(res => {
if (res.list && res.list.length) {
this.fileContent = res.list[0]
} else {
this.fileContent = ''
}
})
} else {
this.fileContent = ''
}
},
// 上传之前的处理
uploadSuccess: function (res) {
if(res.success){
this.$message.success('上传成功!')
}else if(res.message){
this.$message.error(res.message)
}else{
this.$message.error("上传失败")
}
},
// 下载模板
downTemplate(template) {
cmsApi.template_downloadFile(template.templateId, template.templateName);
}
// cmsApi 中与文件上传下载有关的api
export const template_uploadFile_URL = () =>{
return apiUrl+'/cms/template/uploadTemplate/'
};
// 查询文件内容
export const template_queryFile = (id) =>{
return http.requestGet(apiUrl+'/cms/template/queryTemplate/'+id)
};
// 文件下载
export const template_downloadFile = (id,fileName) =>{
// 需要注意的是这一并不是通过http.requestGet 去调用get请求,
// 而是通过 window.location.href的方式
// 否则下载的文件不会直接保存到本地,
// 除了这种方式以外还可以用window.open(url) 去解决这个问题
let url = apiUrl+'/cms/template/downTemplate/'+id+'?fileName='+fileName+'.ftl'
//window.location.href = url
window.open(url)
};