核心问题:
问题就是axios发的请求,浏览器不能直接下载文件
关键点以标红
先看下导出的结果:
非常简单的样式,下面开始介绍如何使用:
我这个例子是,问题+答案的word
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>3.17version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>3.17version>
dependency>
//创建一个word对象
XWPFDocument document = new XWPFDocument();
//创建一个段落(标题)
XWPFParagraph titleParagraph = document.createParagraph();
//将该段落居中显示
titleParagraph.setAlignment(ParagraphAlignment.CENTER);
//XWPFRun是文本对象!!!!!!
XWPFRun title = titleParagraph.createRun();
//设置标题
title.setText("test");
//设置标题加粗
title.setBold(true);
//标题字体大小
title.setFontSize(20);
//换行,同样创建一个段落,再为这个段落创建文本对象,其内容为换行符
XWPFParagraph paragraph3 = document.createParagraph();
XWPFRun paragraphRun3 = paragraph3.createRun();
paragraphRun3.setText("\r");
//创建一个段落,即问题行
XWPFParagraph questionParagraph = document.createParagraph();
XWPFRun questionRun = questionParagraph.createRun();
//设置问题的内容
questionRun.setText((++num)+"."+question.getQuestion());
//问题字体加粗
questionRun.setBold(true);
//问题字体大小
questionRun.setFontSize(12);
//创建一个答案段
XWPFParagraph answerParagraph = document.createParagraph();
//答案我们需要首航缩进,传一个int,480大概就中文两个字,
//578还是多少,就等于1cm,大家可自行调整
//下面注释的那个方法是一样的,setFirstLineIndent会调用setIndentationFirstLine
answerParagraph.setIndentationFirstLine(480);
//answerParagraph.setFirstLineIndent(400);
XWPFRun answerRun = answerParagraph.createRun();
answerRun.setText(question.getAnswer());
answerRun.setFontSize(12);
//一问一答后,换行
XWPFParagraph paragraph2 = document.createParagraph();
XWPFRun paragraphRun2 = paragraph2.createRun();
paragraphRun2.setText("\r");
//将问题和答案段落的创建放进循环
//用数据库的内容填充setText即可
//现在生成word到本地
//先创建一个文件对象,把你要放的路劲丢进去
//文件夹不存在就创建该目录
File file = new File("D:/word");
if (!file.exists()){
file.mkdirs();
}
//创建文件输出流,异常自行捕获、上抛一下,这里就省去
//需要传入你要生成的文件完整路径
FileOutputStream out = new FileOutputStream("D:/word/test.docx");
//用word对象的write方法写入目标文件
document.write(out);
out.close();
document.close();
需要提前告知的是,以下出现的问题及解决方案,均是我在
vue+axios+springboot,前后端分离环境中发生的
问题就是axios发的请求,浏览器不能直接下载文件
ok,开始。
tacotacotacotaco:
我们需要在响应头中添加一些东西,
下面贴一下,我疯狂测试后比较行的,两句:
response.setHeader("Content-Disposition", "attachment; filename="+userId+".docx");
这个Content-Disposition是干什么的呢?
使文件直接在浏览器上显示或者在访问时弹出文件下载对话框(我们这就是让浏览器下载)
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8");
这两句都是等价的,设置content-type就是告诉浏览器这是啥玩意儿,
application/vnd.openxmlformats-officedocument.wordprocessingml.document
这一句是docx后缀的word,
application/msword,这是doc后缀的word
根据你要返回的文档类型不同,你需要设置的MIME类型就不同,这是从别人家找到的常用的:
.doc application/msword
.dot application/msword
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template
.docm application/vnd.ms-word.document.macroEnabled.12
.dotm application/vnd.ms-word.template.macroEnabled.12
.xls application/vnd.ms-excel
.xlt application/vnd.ms-excel
.xla application/vnd.ms-excel
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template
.xlsm application/vnd.ms-excel.sheet.macroEnabled.12
.xltm application/vnd.ms-excel.template.macroEnabled.12
.xlam application/vnd.ms-excel.addin.macroEnabled.12
.xlsb application/vnd.ms-excel.sheet.binary.macroEnabled.12
.ppt application/vnd.ms-powerpoint
.pot application/vnd.ms-powerpoint
.pps application/vnd.ms-powerpoint
.ppa application/vnd.ms-powerpoint
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
.potx application/vnd.openxmlformats-officedocument.presentationml.template
.ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
.ppam application/vnd.ms-powerpoint.addin.macroEnabled.12
.pptm application/vnd.ms-powerpoint.presentation.macroEnabled.12
.potm application/vnd.ms-powerpoint.template.macroEnabled.12
.ppsm application/vnd.ms-powerpoint.slideshow.macroEnabled.12
.mdb application/vnd.ms-access
大家对号入座吧,
顺便说一说我在网上看到的一些其它的请求头设置是干嘛的
//通用的MIME类型,重点----通用
//二进制流
response.setHeader("content-type", "application/octet-stream");
然后用响应流将word对象写回浏览器:
OutputStream outputStream = response.getOutputStream();
document.write(outputStream);
完整的返回代码:(有点长,但就是前面的内容,加上填充的数据)
String userId = (String) dataMap.get("userId");
List<Integer> typeIds = (List<Integer>) dataMap.get("typeIds");
//创建一个word对象
XWPFDocument document = new XWPFDocument();
for (Integer typeId : typeIds) {
int num = 0;
List<QuestionVo> questions = exportMapper.getExportQuestionsByType(userId, typeId);
Type one = new LambdaQueryChainWrapper<>(typeService.getBaseMapper())
.select(Type::getTypeName)
.eq(Type::getId, typeId)
.one();
//创建一个段落
XWPFParagraph titleParagraph = document.createParagraph();
//将其居中成为标题段
titleParagraph.setAlignment(ParagraphAlignment.CENTER);
//创建一个文本对象
XWPFRun title = titleParagraph.createRun();
//设置标题
title.setText(one.getTypeName());
//设置标题加粗
title.setBold(true);
//标题字体大小
title.setFontSize(20);
//换行
XWPFParagraph paragraph3 = document.createParagraph();
XWPFRun paragraphRun3 = paragraph3.createRun();
paragraphRun3.setText("\r");
for (QuestionVo question : questions) {
XWPFParagraph questionParagraph = document.createParagraph();
XWPFRun questionRun = questionParagraph.createRun();
//设置问题段
questionRun.setText((++num)+"."+question.getQuestion());
//问题加粗
questionRun.setBold(true);
//标题字体大小
questionRun.setFontSize(12);
//设置答案段
XWPFParagraph answerParagraph = document.createParagraph();
answerParagraph.setIndentationFirstLine(480);
//answerParagraph.setFirstLineIndent(400);
XWPFRun answerRun = answerParagraph.createRun();
answerRun.setText(question.getAnswer());
answerRun.setFontSize(12);
//换行
XWPFParagraph paragraph2 = document.createParagraph();
XWPFRun paragraphRun2 = paragraph2.createRun();
paragraphRun2.setText("\r");
}
}
File file = new File("D:/word");
if (!file.exists()){
file.mkdirs();
}
try {
//禁用缓存
response.setHeader(HttpHeaders.PRAGMA, "No-cache");
response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache");
response.setHeader("Content-Disposition", "attachment; filename="+userId+".docx");
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8");
//response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8");
OutputStream outputStream = response.getOutputStream();
document.write(outputStream);
outputStream.close();
document.close();
}catch (Exception e){
e.printStackTrace();
}
下面就可以返回给前端了(也是我最大的问题所在)
页面点击导出浏览器无动于衷,来来回回试了很多修改方案都不行,
我是真的想多长几根头发,折磨~~~~
后面我就写了一个测试controller,关闭拦截器,在浏览器直接访问该接口
响应头的效果如下(火狐浏览器):
完全没问题,一气呵成,差点没跳到房顶上去
接着把前端导出的请求改为/test,理所当然,浏览器毫无响应
axios的问题?应该是的
苦思无果,我决定采用另一种办法,js模拟下载,就是axios拿到响应的数据
链接如下:
https://blog.csdn.net/xiaobing_hope/article/details/103363513
大概长这样:
这并不是乱码!
响应的数据在res.data,
我们只需要这样设置axios请求:
添加响应类型,设置为 blob
然后响应回来后,取到word数据,生成下载链接,js模拟点击下载即可
模拟点击下载代码:
const data = res.data
let url = window.URL.createObjectURL(data) // 将二进制文件转化为可访问的url
var a = document.createElement('a')
document.body.appendChild(a)
a.href = url
a.download = sessionStorage.getItem("userName")+'.docx' //文件名
a.click() // 模拟点击下载
window.URL.revokeObjectURL(url) //清除
ok!
最开始使用的就是这个,但是我发现,循环输出数据库的内容到word里面,最后只显示最后一条数据,因为之前的都被覆盖了。
之后看视频找到了一个解决方案:就是将循环的内容拼接起来,传给一个模板占位符。拼接的时候加上换行符即可。但是我一想,我还需要设置首行缩进、设置一些格式,这样做只会更难,而且这样简单的word使用poi也不难