使用office提供的在线预览功能
https://view.officeapps.live....
src后面接你需要预览的文件下载地址.
如果不能实现预览功能,提示报错
请检查http返回的Content-Type是否正确
当从浏览器返回一个文件时,需要指定ContentType,以下是Office2007对应的值:
"application/vnd.openxmlformats-officedocument.wordprocessingml.template" (for .dotx files)
"application/vnd.openxmlformats-officedocument.presentationml.presentation" (for .pptx files)
"application/vnd.openxmlformats-officedocument.presentationml.slideshow" (for .ppsx files)
"application/vnd.openxmlformats-officedocument.presentationml.template" (for .potx files)
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" (for .xlsx files)
"application/vnd.openxmlformats-officedocument.spreadsheetml.template" (for .xltx files)
参考链接:
https://www.cnblogs.com/huang...
office在线预览文档: https://products.office.com/z...
office文件对应的contentType: https://www.cnblogs.com/diony...
bug解决记录:
原来网站中有个需要,是下载Excel.
最开始实现的时候,是使用jsreport来实现,但是该服务收费,并且由于功能太强大了,学习成本高.
因为我们只是想要下载Excel,后面就找了Excel.js这个库来实现.
下载Excel是没问题,生成文件流以后,,直接返回steam,有一天,领导说,需要在线预览Excel?
考虑过几种方案,
- 自己实现解析Excel,直接以表格的样式返回HTML
- 找第三方js库,来实现Excel预览,
- 其他(尽可能少改动现有代码)
在找其他方案的时候,发现,其他Microsoft Office365提供了在线预览的功能,只需要
https://view.officeapps.live....
src后面跟上Excel的下载地址就行.
但是测试了一下,发现并不能成功.因为我们服务器上通过接口下载Excel的URL拼接在上面src后面,不能预览,会报错.
但是,如果是访问静态资源目录,拼接在src后面,是可以的.
比如: http://a.com/excel.xlsx 可以在线预览
https://a.com/download/report... 不能在线预览
我就怀疑是response Header设置问题,但是,不清楚具体是哪个配置项.
最开始以为是https的问题,但是测试后发现,与https无关,https和http协议都能在线预览
也考虑过是不是URL上没用后缀,导致office无法识别该文件是什么类型,但是下载保存的时候,会自动识别出对应的文件后缀名,所以就肯定不是URL上没有后缀名导致的问题.
服务器后端使用的是Node.js,框架用的是express,如果将Excel放置在静态资源文件目录下,是能正常预览.
如果是将文件放在静态资源文件夹下访问,等同于调用express中的res.sendFile(filePath)方法.
我们的接口中,使用
res.set({
'Content-Disposition': 'Attachment; filename="report.xlsx'
});
来设置response的header,保证用户下载文件后存储到本地时默认是Excel后缀.
好,因为基础知识不牢,只能去查看express的源码中sendFile()方法有什么奥秘了
node_modules/express/lib/response.js
res.sendFile = function sendFile(path, options, callback) {
var done = callback;
var req = this.req;
var res = this;
var next = req.next;
var opts = options || {};
if (!path) {
throw new TypeError('path argument is required to res.sendFile');
}
// support function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
if (!opts.root && !isAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
// create file stream
var pathname = encodeURI(path);
var file = send(req, pathname, opts);
// transfer
sendfile(res, file, opts, function (err) {
if (done) return done(err);
if (err && err.code === 'EISDIR') return next();
// next() all but write errors
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
next(err);
}
});
};
可以看到,实际上sendFile调用的是node_modules/send/index.js
方法,
在看send模块中的代码,发现
var mime = require('mime')
SendStream.prototype.type = function type (path) {
var res = this.res
if (res.getHeader('Content-Type')) return
var type = mime.lookup(path)
if (!type) {
debug('no content-type')
return
}
var charset = mime.charsets.lookup(type)
debug('content-type %s', type)
res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : ''))
}
express在发送文件时,会调用上述代码,根据后缀名来获取Content-Type.
在查看mime的源码,会发现在mime中,require了一个type.json,而type.json就是各种后缀名对应的content-type
因为我们原来的代码中,只设置了Content-Disposition,代表该请求返回的是一个文件.
但是office365实际上是通过Content-Type来获取,当前需要预览的是什么类型的文件(word,ppt,Excel),所以,需要设置对应的Content-Type才能正确实现预览功能
mime库中type.json中后缀名对应的ContentType,可以用来做速查表
{
"application/andrew-inset": [
"ez"
],
"application/applixware": [
"aw"
],
"application/atom+xml": [
"atom"
],
"application/atomcat+xml": [
"atomcat"
],
"application/atomsvc+xml": [
"atomsvc"
],
"application/bdoc": [
"bdoc"
],
"application/ccxml+xml": [
"ccxml"
],
"application/cdmi-capability": [
"cdmia"
],
"application/cdmi-container": [
"cdmic"
],
"application/cdmi-domain": [
"cdmid"
],
"application/cdmi-object": [
"cdmio"
],
"application/cdmi-queue": [
"cdmiq"
],
"application/cu-seeme": [
"cu"
],
"application/dash+xml": [
"mpd"
],
"application/davmount+xml": [
"davmount"
],
"application/docbook+xml": [
"dbk"
],
"application/dssc+der": [
"dssc"
],
"application/dssc+xml": [
"xdssc"
],
"application/ecmascript": [
"ecma"
],
"application/emma+xml": [
"emma"
],
"application/epub+zip": [
"epub"
],
"application/exi": [
"exi"
],
"application/font-tdpfr": [
"pfr"
],
"application/font-woff": [
"woff"
],
"application/font-woff2": [
"woff2"
],
"application/geo+json": [
"geojson"
],
"application/gml+xml": [
"gml"
],
"application/gpx+xml": [
"gpx"
],
"application/gxf": [
"gxf"
],
"application/gzip": [
"gz"
],
"application/hyperstudio": [
"stk"
],
"application/inkml+xml": [
"ink",
"inkml"
],
"application/ipfix": [
"ipfix"
],
"application/java-archive": [
"jar",
"war",
"ear"
],
"application/java-serialized-object": [
"ser"
],
"application/java-vm": [
"class"
],
"application/javascript": [
"js",
"mjs"
],
"application/json": [
"json",
"map"
],
"application/json5": [
"json5"
],
"application/jsonml+json": [
"jsonml"
],
"application/ld+json": [
"jsonld"
],
"application/lost+xml": [
"lostxml"
],
"application/mac-binhex40": [
"hqx"
],
"application/mac-compactpro": [
"cpt"
],
"application/mads+xml": [
"mads"
],
"application/manifest+json": [
"webmanifest"
],
"application/marc": [
"mrc"
],
"application/marcxml+xml": [
"mrcx"
],
"application/mathematica": [
"ma",
"nb",
"mb"
],
"application/mathml+xml": [
"mathml"
],
"application/mbox": [
"mbox"
],
"application/mediaservercontrol+xml": [
"mscml"
],
"application/metalink+xml": [
"metalink"
],
"application/metalink4+xml": [
"meta4"
],
"application/mets+xml": [
"mets"
],
"application/mods+xml": [
"mods"
],
"application/mp21": [
"m21",
"mp21"
],
"application/mp4": [
"mp4s",
"m4p"
],
"application/msword": [
"doc",
"dot"
],
"application/mxf": [
"mxf"
],
"application/octet-stream": [
"bin",
"dms",
"lrf",
"mar",
"so",
"dist",
"distz",
"pkg",
"bpk",
"dump",
"elc",
"deploy",
"exe",
"dll",
"deb",
"dmg",
"iso",
"img",
"msi",
"msp",
"msm",
"buffer"
],
"application/oda": [
"oda"
],
"application/oebps-package+xml": [
"opf"
],
"application/ogg": [
"ogx"
],
"application/omdoc+xml": [
"omdoc"
],
"application/onenote": [
"onetoc",
"onetoc2",
"onetmp",
"onepkg"
],
"application/oxps": [
"oxps"
],
"application/patch-ops-error+xml": [
"xer"
],
"application/pdf": [
"pdf"
],
"application/pgp-encrypted": [
"pgp"
],
"application/pgp-signature": [
"asc",
"sig"
],
"application/pics-rules": [
"prf"
],
"application/pkcs10": [
"p10"
],
"application/pkcs7-mime": [
"p7m",
"p7c"
],
"application/pkcs7-signature": [
"p7s"
],
"application/pkcs8": [
"p8"
],
"application/pkix-attr-cert": [
"ac"
],
"application/pkix-cert": [
"cer"
],
"application/pkix-crl": [
"crl"
],
"application/pkix-pkipath": [
"pkipath"
],
"application/pkixcmp": [
"pki"
],
"application/pls+xml": [
"pls"
],
"application/postscript": [
"ai",
"eps",
"ps"
],
"application/prs.cww": [
"cww"
],
"application/pskc+xml": [
"pskcxml"
],
"application/rdf+xml": [
"rdf"
],
"application/reginfo+xml": [
"rif"
],
"application/relax-ng-compact-syntax": [
"rnc"
],
"application/resource-lists+xml": [
"rl"
],
"application/resource-lists-diff+xml": [
"rld"
],
"application/rls-services+xml": [
"rs"
],
"application/rpki-ghostbusters": [
"gbr"
],
"application/rpki-manifest": [
"mft"
],
"application/rpki-roa": [
"roa"
],
"application/rsd+xml": [
"rsd"
],
"application/rss+xml": [
"rss"
],
"application/rtf": [
"rtf"
],
"application/sbml+xml": [
"sbml"
],
"application/scvp-cv-request": [
"scq"
],
"application/scvp-cv-response": [
"scs"
],
"application/scvp-vp-request": [
"spq"
],
"application/scvp-vp-response": [
"spp"
],
"application/sdp": [
"sdp"
],
"application/set-payment-initiation": [
"setpay"
],
"application/set-registration-initiation": [
"setreg"
],
"app