nginx的access日志自定义格式记录了post请求数据,因为一些原因需要从原始数据恢复出jpg格式图片。

首先处理日志,筛选出含有图片数据的日志条目,取出其中一条进行分析,大致格式如下,为了便于查看,做一下换行处理:

- | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 |
--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9
\x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A
--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9
\x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\abc.jpg\x22
\x0D\x0AContent-Type: application/octet-stream
\x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00 ... \xD2_\xA0\x1A\x7F\xFF\xD9\x0D\x0A
--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--
\x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123

其中,--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9是表单的分隔字段,\x0D\x0A是回车和换行的转义字符,分别等同于\r和\n,\xFF\xD8\xFF\xE0\x00\x10JFIF\x00 ... \xD2_\xA0\x1A\x7F\xFF\xD9部分就是图片原始数据。

使用上述单行日志文件tmp.log进行调试:

>>> f = open('tmp.log', 'rb')
>>> data = f.read()
>>> data
"- | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 | --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\x221512806410245.jpg\x22\x0D\x0AContent-Type: application/octet-stream\x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00 ... \xBC'\xF1\x8C\xCC\x83,\xFAo\xD2_\xA0\x1A\x7F\xFF\xD9
\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123"
>>> print data
- | 09/Dec/2017:08:00:19 +0000 | POST /some/api HTTP/1.1 | 200 | 461 | --SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22name\x22\x0D\x0AContent-Type: text/plain; charset=UTF-8\x0D\x0A\x0D\x0value\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9\x0D\x0AContent-Disposition: form-data; name=\x22file\x22; filename=\x221512806410245.jpg\x22\x0D\x0AContent-Type: application/octet-stream\x0D\x0AContent-Transfer-Encoding: binary\x0D\x0A\x0D\x0A\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00 ... \xBC'\xF1\x8C\xCC\x83,\xFAo\xD2_\xA0\x1A\x7F\xFF\xD9\x0D\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\x0D\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123
>>> f.close()

可以看出,打印到屏幕上的转义字符,如"\x0D\x0A"在文件中的原始字符串实际上是"\\x0D\\x0A"。因为""是用于转义的,所以想要显示反斜杠本身就必须转义自身。也就是说,print的时候,"\"被转义成"",所以我们看到的是"\x0D",实际文件中储存的是"\\0D",而我们真正需要写入文件的,是"\x0D"这个转义字符,而不是"\x0D"这个字符串。

由于文件是以二进制方式打开的,读取到的都是原始的流,所以在匹配的时候需要使用"\\\\"来表示"\\"。

使用re库处理日志:

原始数据前面是Content-Transfer-Encoding: binary加上两个\r\n,后面是一个\r\n跟上表单分割字符串--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--

>>> import re
>>> pf = re.compile('^.*Content-Transfer-Encoding: binary\\\\x0D\\\\x0A\\\\x0D\\\\x0A')
>>> pb = re.compile('\\\\x0D\\\\x0A--.*$')
>>> data = re.sub(pf,'',data)
>>> data
"\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00 ... \\xBC'\\xF1\\x8C\\xCC\\x83,\\xFAo\\xD2_\\xA0\\x1A\\x7F\\xFF\\xD9\\x0D\\x0A--SgX5AyE7dwyg0smH-Tqpt-ggGQwTU9--\\x0D\\x0A | 42097 | - | - | - | 1.1.1.1 | d1fkkbcd02eb | 127.0.0.1:8888 | 0.034 | 0.123"
>>> data = re.sub(pb,'',data)
>>> data
"\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00 ... \\xBC'\\xF1\\x8C\\xCC\\x83,\\xFAo\\xD2_\\xA0\\x1A\\x7F\\xFF\\xD9"

这样的匹配处理并不严谨,假设原始数据中本来就含有\\x0D\\x0A--的话,就会丢失原始数据,但目前为止还没遇到这种情况有效。更麻烦的做法是先匹配出表单分割字符串,然后以此为界分割出数据后,再删除两边多余的字符和回车换行。

成功提取出原始数据后,对数据进行解码,并写入.jpg文件。如果没有解码这一步,所有的数据都会被当做字符串写入文件,而不会被当成转义字符。简单做个测试:

把"\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01"保存到文件test.log。

>>> f = open('test.log', 'rb')
>>> data = f.read()
>>> data
'\\xFF\\xD8\\xFF\\xE0\\x00\\x10JFIF\\x00\\x01\n'
>>> print data
\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01>>> import codecs as c
>>> c.decode(data, 'string_escape')
'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\n'
>>> print c.decode(data, 'string_escape')
����JFIF>>>

读取的原始数据是两根反斜杠,print时因为反斜杠被转义成字符,所以变成一根。解码后的数据本身只有一根斜杠,print时打印出转义字符本身,也就是乱码。

接下来就可以分割文件,然后还原图片了。

#!/usr/bin/env pythonimport codecs, reifile = 'access.log'
suffix = 'jpg'pf = re.compile('^.*Content-Transfer-Encoding: binary\\\\x0D\\\\x0A\\\\x0D\\\\x0A')
pb = re.compile('\\\\x0D\\\\x0A--.*$')try:with open(ifile, 'rb') as f:number = 1 #第一张图片序号为1while True:l = f.readline().strip() #读取一行并去掉末尾的换行符\nif not l: #文件读完返回''时退出循环breakl = re.sub(pf, '', l) #将数据前的字符替换为空l = re.sub(pb, '', l) #将数据后的字符替换为空img_file = '.'.join([str(number), suffix]) #图片文件名称print img_file #打印名字方便看进度with open(img_file, 'wb') as i: #解码并写入文件i.write(codecs.decode(l, 'string_escape'))number += 1 #下一张图片序号加1
except IOError:pass

access.log含有多条日志,每条日志都包含图片数据。日志较大,所以不使用readlines(),本来使用list也很占内存,且这样处理比较慢,要等程序把整个文件读完。按行读取日志后提取出图片数据后解码写入文件即可。

在这里放一个demo日志,方便以后改进和调试。
https://files.cnblogs.com/files/nampp/demo.tar.gz

EOF

转载于:https://www.cnblogs.com/nampp/p/8044085.html

从nginx日志原始二进制数据还原文件相关推荐

  1. b是python文件二进制打开_如何在Python中打开和显示原始二进制数据?

    'rb'模式允许您从Python中的文件读取原始二进制数据:with open(filename, 'rb') as file: raw_binary_data = file.read() type( ...

  2. 【lnmp+ELK+Nginx+GeoIP】超详细新版ELK8.5.0解析收集Nginx日志并实现数据可视化与IP地址地图可视化教程-2022年11月版

    超详细新版ELK8.5.0+lnmp1.9解析收集Nginx日志并实现数据可视化与IP地址地图可视化教程-2022年11月版 前言 1.基础环境和所需要的软件包 1.1服务器配置(仅供参考) 1.2 ...

  3. 12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间

    - 12.10 Nginx访问日志 - 12.11 Nginx日志切割 - 12.12 静态文件不记录日志和过期时间# 12.10 Nginx访问日志 - 日志的格式- vim /usr/local/ ...

  4. 6月8日任务(12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件)

    课程名称:12.10 Nginx访问日志 笔记内容: [root@localhost ~]# vim /usr/local/nginx/conf/nginx.conf 定义日志格式 combined_ ...

  5. h5如何上传文件二进制流_前端H5中JS用FileReader对象读取blob对象二进制数据,文件传输...

    HTML5中的Blob对象只是二进制数据的容器,本身并不能操作二进制,故本篇将对其操作对象FileReader进行介绍. FileReader FileReader主要用于将文件内容读入内存,通过一系 ...

  6. pacbio 原始下机数据h5 文件简介

    pacbio 采用hdf5文件格式保存原始的下机数据,对于RS 测序系统而言,会产生一个 bas.h5 的文件; 以bas.h5 文件为例,看一下有下机数据中保存了那些信息 h5dump 工具可以用来 ...

  7. ofstream文件输出流把二进制数据写入文件

    1.添加头文件 #include <fstream> #include <sstream>using namespace std; 2.执行文件打开写入关闭操作 //在实际应用 ...

  8. python编程实现将文本音频数据还原为wav语音文件

    主要功能 主要实现将文本音频数据转为wav文件. 通过我上一个博客的python编程读取wav文件数据得到的txt格式音频数据,再通过此程序实现将之前的文本音频数据再还原为wav语音文件. 程序 程序 ...

  9. 记一次从Sql Server中图片二进制流还原回图片的开发过程

    背景:最近在客户现场做项目上线时,需要数据迁移工作.客户之前用的一个BS桌面应用,其中关于图片的存储全部以二进制流的方式写入到Sql Server数据库表中的某个字段中,如下图所示,由于新开发应用采用 ...

最新文章

  1. 利用C语言实现顺序表
  2. JavaScript权威设计--CSS(简要学习笔记十六)
  3. oracle中右击出现的含义,Oracle中经典的问题解决方案-Oracle
  4. 四种python 单继承的实现方式
  5. 听说Attention与Softmax更配哦~
  6. java合并整形_Java:合并InputStreams(Java: merging InputStreams)
  7. 前端程序员书桌上不可缺少的CSS书籍
  8. 浅谈ajax中get与post的区别,以及ajax中的乱码问题的解决方法
  9. ssm访问html乱码_SpringMVC+jdbc和SSM整合
  10. Java并发(二)——ThreadLocal
  11. 马化腾:5G和AI双核驱动下,产业互联网的春天来了
  12. REDHAT5.6dhcp服务器及dhcp中继代理配置
  13. 【免费毕设】ASP.NET+SQL通用作业批改系统设计(源代码+论文)
  14. css精灵图如何取图
  15. jQuery入门基础
  16. Spring Tool Suite开发环境搭建
  17. jsp技术被淘汰了?那还要不要学它?
  18. MOBA移动游戏性能分析报告:渲染、UI和逻辑代码是性能头号杀手!
  19. 快递面单隐私保护教程
  20. nib but the view outlet was not set 错误的解决办法。

热门文章

  1. virtualenvvirtualenvwrapper on WindowsUbuntu
  2. 重在参与吗。。。。。。。。
  3. [临时]关于tar.gz .gz的压缩研究
  4. 如何创建企业微信应用
  5. selenium中webdriver跳转新页面后定位置新页面的两种方式
  6. 实验8-SPSS交叉表分析
  7. CentOS-7.2、7.5模板机制作
  8. NFS网络文件共享存储服务器
  9. 《微信小程序:开发入门及案例详解》—— 3.4 小结
  10. MySQL 5.1中IN查询不要用到NULL条件