请不要理解,因为我在不知道所有背景的情况下被扔到了这个项目的中间。如果你有WTF问题,请相信我,我也有。
以下是该场景:我有一堆文件驻留在IIS服务器上。他们没有文件扩展名。只是名称为“asda-2342-sd3rs-asd24-ut57”等的裸文件。没什么直观的。
问题是我需要在ASP.NET(2.0)页面上提供文件,并将tiff文件显示为tiff,将PDF文件显示为PDF。不幸的是,我不知道哪个是哪个,我需要能够以各自的格式适当地显示它们。
例如,假设我需要显示2个文件,一个是tiff,一个是PDF。该页面应该显示一个tiff图像,也许还有一个链接可以在新的标签/窗口中打开PDF。
问题:
由于这些文件都是无扩展名,因此我必须强制IIS将所有内容作为TIFF提供。但是,如果我这样做,PDF文件将不会显示。我可以更改IIS以强制MIME类型为未知文件扩展名的PDF,但我有相反的问题。
http://support.microsoft.com/kb/326965
这个问题比我想象的容易吗?还是像我期待的那样令人讨厌?
答案 0 :(得分:22)
好的,有足够多的人弄错了我要发布一些我必须识别TIFF的代码:
private const int kTiffTagLength = 12;
private const int kHeaderSize = 2;
private const int kMinimumTiffSize = 8;
private const byte kIntelMark = 0x49;
private const byte kMotorolaMark = 0x4d;
private const ushort kTiffMagicNumber = 42;
private bool IsTiff(Stream stm)
{
stm.Seek(0);
if (stm.Length < kMinimumTiffSize)
return false;
byte[] header = new byte[kHeaderSize];
stm.Read(header, 0, header.Length);
if (header[0] != header[1] || (header[0] != kIntelMark && header[0] != kMotorolaMark))
return false;
bool isIntel = header[0] == kIntelMark;
ushort magicNumber = ReadShort(stm, isIntel);
if (magicNumber != kTiffMagicNumber)
return false;
return true;
}
private ushort ReadShort(Stream stm, bool isIntel)
{
byte[] b = new byte[2];
_stm.Read(b, 0, b.Length);
return ToShort(_isIntel, b[0], b[1]);
}
private static ushort ToShort(bool isIntel, byte b0, byte b1)
{
if (isIntel)
{
return (ushort)(((int)b1 << 8) | (int)b0);
}
else
{
return (ushort)(((int)b0 << 8) | (int)b1);
}
}
我为了得到这个而破解了一些更通用的代码。
对于PDF,我的代码如下:
public bool IsPdf(Stream stm)
{
stm.Seek(0, SeekOrigin.Begin);
PdfToken token;
while ((token = GetToken(stm)) != null)
{
if (token.TokenType == MLPdfTokenType.Comment)
{
if (token.Text.StartsWith("%PDF-1."))
return true;
}
if (stm.Position > 1024)
break;
}
return false;
}
现在,GetToken()是对扫描程序的调用,它将Stream标记为PDF标记。这不重要,所以我不打算在这里粘贴它。我正在使用tokenizer而不是查看substring来避免这样的问题:
% the following is a PostScript file, NOT a PDF file
% you'll note that in our previous version, it started with %PDF-1.3,
% incorrectly marking it as a PDF
%
clippath stroke showpage
此代码被上面的代码段标记为非PDF,而更简单的代码块将错误地将其标记为PDF。
我还应该指出,目前的ISO规范没有以前Adobe拥有的规范中的实现说明。最重要的是来自PDF参考,版本1.6:
Acrobat viewers require only that the header appear somewhere within
the first 1024 bytes of the file.
答案 1 :(得分:8)
可以通过查看第一个字节http://local.wasp.uwa.edu.au/~pbourke/dataformats/tiff/
来检测TIFF前8个字节构成标题。 其中前两个字节是 “II”用于小端字节排序 或“MM”表示大端字节排序。
关于PDF:http://www.adobe.com/devnet/livecycle/articles/lc_pdf_overview_format.pdf
标题只包含一行 标识PDF的版本。 示例:%PDF-1.6
答案 2 :(得分:4)
阅读每种文件格式的规范将告诉您如何识别该格式的文件。
TIFF个文件 - 检查字节1和2是否为0x4D4D或0x4949 和字节2-3,值为'42'。
规范的第13页写着:
TIFF文件以8字节开头 图像文件头,包含 以下信息:字节0-1: 文件中使用的字节顺序。法律 值是:“II”(4949.H)“MM” (4D4D.H)在“II”格式中,字节 订单始终是最少的 最重要的字节 有效字节,对于16位和 32位整数这是调用的 little-endian字节顺序。在“MM”中 格式,字节顺序总是最多的 重要的,最不重要的 16位和32位整数。这个 被称为big-endian字节顺序。字节 2-3任意但经过精心挑选 数字(42)进一步确定 该文件作为TIFF文件。字节 顺序取决于字节的值 0-1。
PDF文件以PDF版本开头,后跟几个二进制字节。 (我想你现在必须购买当前版本的ISO规范。)
第7.5.2节
PDF文件的第一行应为 由5组成的标题 字符%PDF-后跟一个版本 形式1.N的数字,其中N是a 0到7之间的数字。符合 读者应接受任何文件 以下标题:%PDF-1.0, %PDF-1.1,%PDF-1.2,%PDF-1.3,%PDF-1.4, %PDF-1.5,%PDF-1.6,%PDF-1.7开头 使用PDF 1.4,版本中的条目 文档的目录词典(位于 通过文件中的Root条目 预告片,如7.5.5“文件中所述 拖车“),如果存在,应使用 而不是在中指定的版本 标题。
如果PDF文件包含二进制数据,则为 大多数人(见7.2,“词汇 约定“),标题行应 紧接着是评论 包含至少四个二进制的行 字符 - 即其字符 代码是128或更大。这确保了 文件传输的正确行为 检查附近数据的应用程序 一个文件的开头来确定 是否对待文件的内容 作为文本或二进制文件。
当然,您可以通过查看更多文件特定项目来对每个文件进行“更深入”的检查。
答案 3 :(得分:3)
在内部,文件头信息应该有所帮助。如果你打开一个低级文件,比如StreamReader()或FOPEN(),请查看文件中的前两个字符......几乎每种文件类型都有自己的签名。
PDF always starts with "%P" (but more specifically would have like %PDF)
TIFF appears to start with "II"
Bitmap files with "BM"
Executable files with "MZ"
我过去也必须处理这个问题...也有助于防止不需要的文件上传到指定的网站,并在检查后立即中止。
编辑 - 发布示例代码以读取和测试文件头类型
String fn = "Example.pdf";
StreamReader sr = new StreamReader( fn );
char[] buf = new char[5];
sr.Read( buf, 0, 4);
sr.Close();
String Hdr = buf[0].ToString()
+ buf[1].ToString()
+ buf[2].ToString()
+ buf[3].ToString()
+ buf[4].ToString();
String WhatType;
if (Hdr.StartsWith("%PDF"))
WhatType = "PDF";
else if (Hdr.StartsWith("MZ"))
WhatType = "EXE or DLL";
else if (Hdr.StartsWith("BM"))
WhatType = "BMP";
else if (Hdr.StartsWith("?_"))
WhatType = "HLP (help file)";
else if (Hdr.StartsWith("\0\0\1"))
WhatType = "Icon (.ico)";
else if (Hdr.StartsWith("\0\0\2"))
WhatType = "Cursor (.cur)";
else
WhatType = "Unknown";
答案 4 :(得分:3)
Gary Kessler提供了一个非常有用的文件签名列表,即“神奇数字”http://www.garykessler.net/library/file_sigs.html
答案 5 :(得分:1)
如果你去here,你会发现TIFF通常以“幻数”0x49 0x49 0x2A 0x00开始(还有一些其他定义),这是文件的前4个字节。
因此,只需使用前4个字节来确定文件是否为TIFF。
编辑,以其他方式做到这一点可能更好,并首先检测PDF。 PDF的神奇数字更加标准化:正如Plinth指出的那样,它们以“%PDF”开头,位于前1024字节(0x25 0x50 0x44 0x46)的某处。 source答案 6 :(得分:0)
您将不得不写一个ashx来获取所请求的文件。
然后,你的处理程序应该读取前几个字节(或左右)以确定文件类型到底是什么 - PDF和TIFF在文件的开头有“魔术数字”,你可以用来确定它,然后相应地设置您的响应标头。
答案 7 :(得分:0)
你可以使用Myrmec来识别文件类型,这个库使用文件字节头。这个库可用于nuget“Myrmec”,这是repo,myrmec也支持mime类型,你可以尝试一下。代码会是这样的:
// create a sniffer instance.
Sniffer sniffer = new Sniffer();
// populate with mata data.
sniffer.Populate(FileTypes.CommonFileTypes);
// get file head byte, may be 20 bytes enough.
byte[] fileHead = ReadFileHead();
// start match.
List<string> results = sniffer.Match(fileHead);
并获取mime类型:
List<string> result = sniffer.Match(head);
string mimeType = MimeTypes.GetMimeType(result.First());
但是支持tiff只有“49 49 2A 00”和“4D 4D 00 2A”两个签名,如果你有更多你可以添加自己,可能你可以看到myrmec的自述文件求助。 myrmec github repo