2010-04-28 62 views
16

請在我不知道所有背景的情況下把我帶入這個項目的中間。如果你有WTF問題,請相信我,我也有。如何檢測文件是PDF還是TIFF?

這裏是場景:我有一堆文件駐留在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

這方面的問題更容易比我想還是因爲討厭,因爲我期待?

回答

19

OK,足夠多的人收到此錯誤,我要去發佈一些代碼,我有識別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()是調用成一個標記化流成PDF令牌的掃描儀。這是不平凡的,所以我不會在這裏粘貼它。我使用的分詞器,而不是看着子,以避免這樣的問題:

% 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. 
+0

謝謝!我今晚會檢查一下 – eviljack 2010-04-28 19:59:07

+0

真棒,它的工作原理! – eviljack 2010-04-28 20:11:19

+0

stm.Seek(0);我失敗了,沒有編譯。我使用vs 2008,.net 3.5。 – Kiquenet 2010-07-21 20:43:55

0

如果你去here,你會看到TIFF通常以「幻數」0x49 0x49 0x2A 0x00(也給出其他一些定義)開始,這是文件的前4個字節。

所以只需使用這些前4個字節來確定文件是否是TIFF。

編輯,它可能更好地做到另一種方式,並首先檢測PDF。 PDF的神奇數字更加標準化:正如Plinth指出的那樣,它們在前1024個字節(0x25 0x50 0x44 0x46)的某個地方以「%PDF」開頭。 source

+0

這個神奇的數字取決於little/big endian。 – Andrey 2010-04-28 17:56:13

+1

這很接近,但錯了。 TIFF以兩個簽名之一開始,即0x49 0x49 0x2a 0x00或0x4d 0x4d 0x00 0x2a。 – plinth 2010-04-28 17:57:07

+0

您的PDF檢查也是錯誤的。 %PDF只需要出現在前1024個字節中。 – plinth 2010-04-28 18:29:21

8

TIFF可以通過在第一字節偷看http://local.wasp.uwa.edu.au/~pbourke/dataformats/tiff/

的前8個字節形成首部被檢測。 前兩個字節爲 「II」,用於小端字節排序 或「MM」用於大端字節排序。

關於PDF:http://www.adobe.com/devnet/livecycle/articles/lc_pdf_overview_format.pdf

頭只包含一個行 標識PDF版本。 示例:%PDF-1.6

+0

來自Adobe的文檔並不完全確切的說明。 %PDF-1.x,其中x是一個數字,可能出現在文件的前1K內的任何位置。 – plinth 2010-04-28 17:59:04

+0

好吧,這裏是最完整的規範http://www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf它是> 30 Mb – Andrey 2010-04-28 18:03:41

2

在內部,文件頭信息應該有所幫助。如果您打開低級別文件(例如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"; 
+0

mr.DRapp,任何示例代碼?? – Kiquenet 2010-07-22 16:30:20

+0

@alhambraeidos - 我通過C#發佈了更新後的代碼示例 – DRapp 2010-07-22 17:46:00

+0

不應該在答案的關鍵部分寫作「似乎開始於」!根據規範,TIFF文件以2個字節的ASCII「II」或「MM」開頭,後面跟着(II)英特爾小端或2個字節(MM)摩托羅拉大端字節順序,形成整數42. – Spike0xff 2014-03-21 20:24:57

0

你將不得不編寫一個ASHX獲得請求的文件。

然後,您的處理程序應該讀取前幾個字節(或左右)以確定文件類型的真實含義--PDF和TIFF在文件的開頭有「magic numers」,您可以使用它來確定該文件的類型,然後相應地設置您的響應標題。

4

閱讀說明書每種文件格式會告訴你如何識別這種格式的文件。

TIFF文件 - 檢查字節1和2的0x4D4D或0x4949 字節2-3的值爲'42'。

第13頁的規格的讀取:

的TIFF文件的開頭是一個8字節的 圖像文件頭,包含 以下信息:字節0-1:文件內使用的 字節順序。法律 的值爲:「II」(4949.H)「MM」 (4D4D.H)在「II」格式中,字節 的順序總是從最少的 有效字節到最有效的字節,對於16位位和 32位整數這稱爲 小端字節順序。在「MM」 格式中,對於 這兩種16位和32位整數,字節順序總是從大多數 重要到最不重要。這個 被稱爲big-endian字節順序。字節 2-3任意但仔細選擇的 數字(42),其進一步將該文件標識爲TIFF文件 。字節 的順序取決於字節 0-1的值。

PDF文件以PDF版本開頭,接着是幾個二進制字節。 (我想你現在必須購買ISO規格爲最新版本。)

第7.5.2節

PDF文件的第一行應 頭由5個 字符% PDF格式,後面跟着格式爲1.N的版本 ,其中N是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, 文檔的目錄字典中的版本條目(位於 ,通過文件的 預告片中的根條目,如7.5.5「文件 預告片」中所述)應使用 而不是指定的版本在 的標題。

如果PDF文件中包含的二進制數據,如 大多數人(見7.2節「詞彙 約定」),標題行應 緊跟含有至少四個二進制 評論 行的字符 - 即,其代碼爲128或更大的字符。這確保 文件傳輸的正確行爲 檢查數據附近的數據的應用程序 確定 是否將文件的內容 作爲文本或二進制文件處理。

當然,您可以通過檢查更多的文件特定項目來對每個文件進行「更深入」的檢查。

+0

任何示例代碼,roygbiv? – Kiquenet 2010-07-21 21:05:01

0

可以使用Myrmec識別文件類型,使用這個庫文件字節頭。這個庫可用於nuget「Myrmec」,這是回購,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); 

串mime類型= MimeTypes.GetMimeType(result.First());

但支持蒂芙只有「49 49 2A 00」和「4D 4D 00 2A」兩個簽名,如果你有更多的你可以添加你的自我,可能你可以看到myrmec的自述文件尋求幫助。 myrmec github repo

相關問題