Pull to refresh

Определение MIME-типов

Reading time 3 min
Views 61K
Привет, хабр!

Недавно задался вопросом а сколько байт необходимо для корректного определения mime типа файла. В первую очередь погуглив, полученными ответами не удовлетворился и поэтому решил сам провести маленькое исследование на эту тему.

На изучение данного вопроса меня натолкнула следующая задача: определение MIME-типа файла, находящегося на smb-сервере. Лучшее, что я придумал — копировать кусок файла на локальную машину и потом, по этой части пытаться распознать его MIME-тип.



Для начала расскажу, что я нагуглил и почему мне это не понравилось:



Stack Overflow дает 2 ссылки на википедию:

  1. File Signature говорит, что в большинстве случаев достаточно 2-4 байтов. Однако к сожалению это не так, например, для такого популярного формата, как pdf.
  2. List of signatures приводит некоторый список подписей для файлов разных форматов, но он далеко не полный. Потом нашел File Signatures, здесь вроде как все.
    Однако вернемся все к тому же pdf. Если верить этим источника, то для определения того, что файл является pdf достаточно четырех байт (0x25 0x50 0x44 0x46), однако исходя из первых четырех байтов libmagic говорил, что MIME-тип pdf-файла — text/plain, а из пяти — верное application/pdf. Затрудняюсь точно ответить с чем это связано, надо смотреть исходники.


Теперь давайте перейдем, собственно говоря, к тому, что сделал я. Я написал очень маленькую программку, которая считывала все файлы из одной директории, копировала первые N байт в другую директорию, а затем по частичным копиям полученных файлов пыталась определить, а что это собственно говоря такое было. И так до тех пор, пока MIME-тип части файла не совпадет с MIME-типом оригинала. По результатам работы программа рапортовала, сколько байт понадобилось для определения того или иного типа. Вот ее код:

#include <stdio.h>
#include <stdlib.h>
#include <magic.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define TEST_DIR "test-dir/"
#define TMP_DIR "tmp-dir/"

magic_t cookie;

// Detects how many bytes required for correct MIME-type of this file
void detect_size(char *filename) {
  int bytes = 1;
  int infd, outfd;
  char strin[100], strout[100], type[100];
  char buf[4096];

  strcpy(strin, TEST_DIR);
  strcat(strin, filename);

  strcpy(strout, TMP_DIR);
  strcat(strout, filename);

  while(1) {
    // Make a copy of given file
    infd = open(strin, O_RDONLY);
    outfd = open(strout, O_RDWR | O_CREAT, 00666);
    read(infd, &buf, bytes);
    write(outfd, &buf, bytes);
    lseek(infd, 0, SEEK_SET);
    lseek(outfd, 0, SEEK_SET);

    // Detect mime types of old and new 
    const char *mime_type = magic_descriptor(cookie, infd);
    strcpy(type, mime_type);
    mime_type = magic_descriptor(cookie, outfd);

    // Check if mime type detected correctly
    if (strcmp(mime_type, type) == 0) {
      printf("%s detected correctly in %d bytes\n", type, bytes);
      unlink(strout);
      return;
    }

    unlink(strout);
    bytes++;
  }
}

int main() {
  DIR *dirfd = opendir(TEST_DIR);
  struct dirent entry, *result = NULL;

  cookie = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR);
  magic_load(cookie, NULL);

  while(1) {
    readdir_r(dirfd, &entry, &result);

    if (result == NULL)
      break;  // No more entries in this directory

    if (!strcmp(entry.d_name, ".") || !strcmp(entry.d_name, ".."))
      continue;  // Ignore "." and ".."

    detect_size(entry.d_name);
  }

  magic_close(cookie);
  closedir(dirfd);
  exit(EXIT_SUCCESS);
}


Потом накидав кучку разных файлов в папку test-dir я начал экспериментировать. Конечно то, что я сделал ни как не тянет на полномасштабное и серьезное исследование, но некоторые результаты все таки интересны. Приведи их краткую сводку:

application/x-sharedlib detected correctly in 18 bytes
application/msword detected correctly in 1793 bytes
image/gif detected correctly in 4 bytes
application/zip detected correctly in 4 bytes
application/x-dosexec detected correctly in 2 bytes
application/vnd.oasis.opendocument.presentation detected correctly in 85 bytes
text/html detected correctly in 14 bytes
image/jpeg detected correctly in 2 bytes
application/x-executable detected correctly in 18 bytes
text/x-makefile detected correctly in 1594 bytes
application/x-executable detected correctly in 18 bytes
application/x-gzip detected correctly in 2 bytes
audio/mpeg detected correctly in 2291 bytes
text/x-c detected correctly in 27 bytes
audio/x-flac detected correctly in 4 bytes
application/pdf detected correctly in 5 bytes

Отмечу некоторые вещи, которые мне показались интересными:

  • Ну во-первых, конечно уже упомянутый pdf, который распознается в 5 байт, а не в 4, как вроде бы следовало ожидать.
  • И напоследок хочется отметить, что не смотря на всю крутость идеи определять тип файла по первым N байтам она, на мой взгляд, провалилась.


Ну это пожалуй все, что я хотел рассказать в этот раз, не люблю много писать. Надеюсь, что это статья окажется кому-нибудь интересной.
Спасибо за внимание.
Tags:
Hubs:
+12
Comments 25
Comments Comments 25

Articles