在平时的工做或学习中,咱们常常会接触不一样格式的文档类型,好比txt,log,Offices文档,编程代码脚本,图片,视频等。本文将会介绍笔者的一个朴素想法,即把不一样格式的文档都放在同一个平台中进行预览,这样既方便查看常见文档,又能提高工做和学习效率。
本项目的工程结构以下:
本项目如今已支持8种文档格式的格式,分别为:javascript
首先,咱们须要下载前端的PDF预览JS框架PDF.js
,它是一个网页端的PDF文件解析和预览框架,下载网址为:http://mozilla.github.io/pdf.js/ 。
接着,本项目还用到了showdown.js
,该JS框架用于渲染Markdown文档。
用Python作后端,tornado为web框架,笔者使用的版本为5.1.1
。css
咱们下载PDF.js
项目代码,并在/pdfjs/web
目录下新建files
文件夹,用于存放上传的文件。为了可以用PDF.js
实现PDF文件预览,须要切换至pdfjs文件夹,运行搭建文件服务器命令:html
python -m http.server 8081
或者:前端
python -m SimpleHTTPServer 8081
接着介绍HTML文件,index.html
是首页代码,主要实现文件上传功能,代码以下:java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body> <div align="center"> <br><br> <h1>文件上传</h1> <form action='file' enctype="multipart/form-data" method='post'> <div class="am-form-group am-form-file"> <input id="doc-form-file" type="file" name="file" multiple> </div> <div id="file-list"></div> <p> <button type="submit" class="am-btn am-btn-default">提交</button> </p> </form> </div> </body> </html>
页面以下(有点儿过于简单,还好本项目是注重文档预览功能):
markdown.html
主要用于展现Markdown文件中的内容,代码以下:python
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Markdown文件展现</title> <script src="https://cdn.bootcss.com/showdown/1.9.0/showdown.min.js"></script> <script> function convert(){ var converter = new showdown.Converter(); var text = "{{ md_content }}"; var html = converter.makeHtml(text.replace(/newline/g, "\n")); document.getElementById("result").innerHTML = html; } </script> </head> <body onload="convert()"> <div id="result" ></div> </body> </html>
注意,咱们在head部分引用了showdown.js
的CDN地址,这样就不用下载该项目文件了。
最后是后端部分,采用Python的Tornado模块实现。tornado_file_receiver.py
主要用于文档的上传和保存,并展现文档内容,完整代码以下:git
# -*- coding: utf-8 -*- import os import logging import traceback import tornado.ioloop import tornado.web from tornado import options from parse_file import * # 文档上传与解析 class UploadFileHandler(tornado.web.RequestHandler): # get函数 def get(self): self.render('upload.html') def post(self): # 文件的存放路径 upload_path = os.path.join(os.path.dirname(__file__), 'pdfjs/web/files') # 提取表单中‘name’为‘file’的文件元数据 # 暂时只支持单文档的上传 file_meta = self.request.files['file'][0] filename = file_meta['filename'] # 保存文件 with open(os.path.join(upload_path, filename), 'wb') as up: up.write(file_meta['body']) text = file_meta["body"] # 解析文件的内容 mtype = file_meta["content_type"] logging.info('POST "%s" "%s" %d bytes', filename, mtype, len(text)) if mtype in ["text/x-python", "text/x-python-script"]: self.write(parse_python(str(text, encoding="utf-8"))) elif mtype in ["text/plain", "text/csv"]: self.write(parse_text_plain(str(text, encoding="utf-8"))) elif mtype == "text/html": self.write(str(text, encoding="utf-8")) elif mtype.startswith("image"): self.write(parse_image(mtype, text)) elif mtype == "application/json": self.write(parse_application_json(str(text, encoding="utf-8"))) elif mtype == "application/pdf": self.redirect("http://127.0.0.1:8081/web/viewer.html?file=files/%s" % filename) elif mtype == "application/octet-stream" and filename.endswith(".md"): self.render("markdown.html", md_content=r"%s" % str(text, encoding="utf-8").replace("\n", "newline")) else: # 其他文件格式 try: self.write(str(text, encoding="utf-8").replace("\n", "<br>")) except Exception: logging.error(traceback.format_exc()) self.write('<font color=red>系统不支持的文件解析格式!</font>') def make_app(): return tornado.web.Application([(r"/file", UploadFileHandler)], template_path=os.path.join(os.path.dirname(__file__), "templates")) # 模板路径 if __name__ == "__main__": # Tornado configures logging. options.parse_command_line() app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
parse_file.py
用于解析各类格式的文档,并返回HTML展现的格式,完整代码以下:github
# -*- coding: utf-8 -*- # author: Jclian91 # place: Pudong Shanghai # time: 2020/6/5 1:05 下午 # filename: parse_file.py # 用于解析各类文件类型的数据 import json import base64 import logging import traceback from json import JSONDecodeError # 解析text/plain或者text/csv文件格式 def parse_text_plain(text): return "<html><head></head><body>%s</body></html>" % text.replace("\n", "<br>") # 解析application/json文件格式 def parse_application_json(text): try: data_dict = json.loads(text) return json.dumps(data_dict, ensure_ascii=False, indent=2).replace("\n", "<br>").replace(" ", " ") except JSONDecodeError: try: data_list = [json.loads(_) for _ in text.split("\n") if _] return json.dumps(data_list, ensure_ascii=False, indent=2).replace("\n", "<br>").replace(" ", " ") except JSONDecodeError: logging.error(traceback.format_exc()) return "JSON文件格式解析错误" except Exception as err: logging.error(traceback.format_exc()) return "未知错误: %s" % err # 解析image/*文件格式 def parse_image(mtype, text): return '<html><head></head><body><img src="data:%s;base64,%s"></body></html>' % \ (mtype, str(base64.b64encode(text), "utf-8")) # 解析Python文件 def parse_python(text): # indent和换行 text = text.replace("\n", "<br>").replace(" ", " ").replace("\t", " " * 4) # 关键字配色 color_list = ["gray", "red", "green", "blue", "orange", "purple", "pink", "brown", "wheat", "seagreen", "orchid", "olive"] key_words = ["self", "from", "import", "def", ":", "return", "open", "class", "try", "except", '"', "print"] for word, color in zip(key_words, color_list): text = text.replace(word, '<font color=%s>%s</font>' % (color, word)) colors = ["peru"] * 7 punctuations = list("[](){}#") for punctuation, color in zip(punctuations, colors): text = text.replace(punctuation, '<font color=%s>%s</font>' % (color, punctuation)) html = "<html><head></head><body>%s</body></html>" % text return html
下面将进一步介绍各类格式实现预览的机制。web
html文件的MIMETYPE为text/html
,因为本项目采用HTML展现,所以对于text/html
的文档,直接返回其内容就能够了。
从Tornado的代码中咱们能够看出,filename变量为文档名称,text为文档内容,bytes字符串。在前端展现的时候,咱们返回其文档内容:编程
self.write(str(text, encoding="utf-8"))
其中,str(text, encoding="utf-8")
是将bytes字符串转化为UTF-8编码的字符串。
txt/log等文件的MIMETYPE为text/plain
,其与HTML文档的不一样之处在于,若是须要前端展现,须要在返回的字符中添加HTML代码,以下(parse_file.py
中的代码):
# 解析text/plain或者text/csv文件格式 def parse_text_plain(text): return "<html><head></head><body>%s</body></html>" % text.replace("\n", "<br>")
csv格式文件的MIMETYPE为text/csv
,其预览的方式与txt/log等格式的文档一致。
但csv是逗号分隔文件,数据格式是表格形式,所以在前端展现上应该有更好的效果。关于这一格式的文档,其前端预览的更好方式能够参考文章: 利用tornado实现表格文件预览 。
关于json文件的预览,笔者更关注的是json文件的读取。这里处理两种状况,一种是整个json文件就是json字符串,另外一种状况是json文件的每一行都是json字符串。在前端展现的时候,采用json.dumps中的indent参数实现缩进,并转化为html中的空格,实现方式以下(parse_file.py
中的代码):
# 解析application/json文件格式 def parse_application_json(text): try: data_dict = json.loads(text) return json.dumps(data_dict, ensure_ascii=False, indent=2).replace("\n", "<br>").replace(" ", " ") except JSONDecodeError: try: data_list = [json.loads(_) for _ in text.split("\n") if _] return json.dumps(data_list, ensure_ascii=False, indent=2).replace("\n", "<br>").replace(" ", " ") except JSONDecodeError: logging.error(traceback.format_exc()) return "JSON文件格式解析错误" except Exception as err: logging.error(traceback.format_exc()) return "未知错误: %s" % err
笔者相信必定有json文件更好的前端展现方式,这里没有采用专门处理json的JS框架,这之后做为后续的改进措施。
PDF文档的展现略显复杂,本项目借助了PDF.js
的帮助,咱们须要它来搭建PDF预览服务,这点在上面的项目代码
部分的开头已经讲了。
搭建好PDF预览服务后,因为上传的文件都会进入pdfjs/web/files
目录下,所以PDF文档预览的网址为:http://127.0.0.1:8081/web/viewer.html?file=files/pdf_name ,其中pdf_name为上传的PDF文档名称。
有了这个PDF预览服务后,咱们展现PDF文档的代码就很简单了(tornado_file_receiver.py
中的代码):
elif mtype == "application/pdf": self.redirect("http://127.0.0.1:8081/web/viewer.html?file=files/%s" % filename)
Python脚本的处理方式并不复杂,无非是在把Python文档转化为HTML文件格式的时候,加入缩进、换行处理,以及对特定的Python关键字进行配色,所以代码以下(parse_file.py
中的代码):
# 解析Python文件 def parse_python(text): # indent和换行 text = text.replace("\n", "<br>").replace(" ", " ").replace("\t", " " * 4) # 关键字配色 color_list = ["gray", "red", "green", "blue", "orange", "purple", "pink", "brown", "wheat", "seagreen", "orchid", "olive"] key_words = ["self", "from", "import", "def", ":", "return", "open", "class", "try", "except", '"', "print"] for word, color in zip(key_words, color_list): text = text.replace(word, '<font color=%s>%s</font>' % (color, word)) colors = ["peru"] * 7 punctuations = list("[](){}#") for punctuation, color in zip(punctuations, colors): text = text.replace(punctuation, '<font color=%s>%s</font>' % (color, punctuation)) html = "<html><head></head><body>%s</body></html>" % text return html
根据笔者的了解,其实有更好的Python脚本内容的预览方式,能够借助handout
模块实现,这点笔者将会在后续加上。
图片文件在HTML上的展现有不少中,笔者采用的方式为:
<img src="data:image/png;base64,ABKAMNDKSJFHVCJSNVOIEJHVUEHVUV==">
就是对图片读取后的字符串进行base64编码便可,所以实现代码以下(parse_file.py
中的代码):
import base64 # 解析image/*文件格式 def parse_image(mtype, text): return '<html><head></head><body><img src="data:%s;base64,%s"></body></html>' % \ (mtype, str(base64.b64encode(text), "utf-8"))
markdown文件的预览稍显复杂,借助showdown.js
和不断的尝试探索,因为markdown在读取后的换行符\n
在转化为JavaScript字符串时并不须要转义,这是实现预览的难点。笔者的作法是把Python读取的markdown中的换行符\n
转化为newline
,并在JS渲染的时候才把newline
替换成\n
,这就解决了不须要转移的难题。具体的实现能够参考markdown.html
,如今Python后端代码中把Python读取的markdown中的换行符\n
转化为newline
,代码以下:
elif mtype == "application/octet-stream" and filename.endswith(".md"): self.render("markdown.html", md_content=r"%s" % str(text, encoding="utf-8").replace("\n", "newline"))
接着在markdown.html
中的JS部分把Python读取的markdown中的换行符\n
转化为newline
,代码以下:
<script> function convert(){ var converter = new showdown.Converter(); var text = "{{ md_content }}"; var html = converter.makeHtml(text.replace(/newline/g, "\n")); document.getElementById("result").innerHTML = html; } </script>
下面将给出上述8中文档格式在本系统中的预览效果。
上传文件为reponse.html
,预览效果以下:
上传文件为info.log,预览效果以下:
上传文件为iris.csv,预览效果以下:
上传文件为test1.json,预览效果以下:
上传文件为,预览效果以下:
上传文件为test.py,预览效果以下:
上传图片为ffe3d40029eae71ccf8587e5dc21d58d.jpg,预览效果以下:
上传文件为Scrapy爬取动态网页.md,预览效果以下:
为了证实上面的预览确实是笔者已经实现的,而不是从哪搬来的图片,特放上程序运行记录以及files目录下的文件,以下:
本项目已经开源至Github,网址为https://github.com/percent4/document_reviewer 。
本项目如今支持的文档格式还比较少,后续能够增长更好文档格式的支持,另外,如今的文档格式的预览有些能够作得更好,后续也会进行优化~
感谢你们阅读,但愿读者多多批评指正~