Разделы публикаций

 
     
 

Загрузка фотографий на сайт c помощью электронной почты

sheremeta_t
115
    sheremeta_t sheremeta_t
Тарас Шеремета
Специализации: 0

Задача


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

Алгоритм


Пользователь отправляет письмо с фотографиями на адрес типа [GUID]@mysite.com, где [GUID] генерируется уникально для каждой пары «пользователь+события» и он (пользователь) копирует / получает этот адрес для отправки после подписки на мероприятие или после регистрации нового профиля. Такого e-mail адреса НЕ СУЩЕСТВУЕТ. По этому все письма отправленные на несуществующие адреса перенаправляем на image_upload@mysite.com. Потом, при считывании почты с этого адреса, мы заспарсим заголовки и узнаем на какой адрес изначально было отправлено письмо. Распарсив полученный адрес, узнаем КУДА складывать файлы и кто их залил.

Использование PEAR


Для работы с POP3 сервером использовалась библиотека PEAR, в частности класс Net_POP3, который находится в файле path/to/pear/Net/POP3.php. Для его успешной работы необходим класс Net_Socket (path/to/pear/Net/Socket.php) и, собственно, PEAR.php. Все эти файлы находятся на сайте http://pear.php.net и доступны для скачивания.
Если PEAR у вас не установлена - просто скопируйте POP3.php и Socket.php в вашу папку с библиотеками и перепропишите пути в них. Файл PEAR.php есть в папке path/to/pear/.

Дополнительные функции


Для обработки прикрепленных файлов мне понадобились еще несколько функций. Не буду приписывать себе их авторство. Они взяты с сайта http://webi.ru/webi_articles/6_12_f.html и немного переделаны (совсем немного).
В моем коде они выглядят так:
/* START FUNCTIONS BLOCK */
// Функция для выдергивания метки boundary из заголовка Content-Type
function get_boundary($ctype){
 if(preg_match('/boundary[ ]?=[ ]?(["]?.*)/i',$ctype,$regs)) {
  $boundary preg_replace('/^\"(.*)\"$/'"\\1"$regs[1]);
  return trim("--$boundary");
 }
}

10 // если письмо будет состоять из нескольких частей (текст, файлы и т.д.)
11 // то эта функция разобьет такое письмо на части (в массив), согласно разделителю boundary
12 function split_parts($boundary,$body) {
13  $startpos strpos($body,$boundary)+strlen($boundary)+2;
14  $lenbody strpos($body,"\r\n$boundary--") - $startpos;
15  $body substr($body,$startpos,$lenbody);
16  return explode($boundary."\r\n",$body);
17 }
18 
19 // Эта функция отделяет заголовки от тела и возвращает массив с заголовками и телом 
20 function fetch_structure($email) {
21  $ARemail = Array();
22  $separador "\r\n\r\n";
23  $header trim(substr($email,0,strpos($email,$separador)));
24  $bodypos strlen($header)+strlen($separador);
25  $body substr($email,$bodypos,strlen($email)-$bodypos);
26  $ARemail["header"] = $header
27  $ARemail["body"] = $body;
28  return $ARemail;
29 }
30 
31 // разбирает все заголовки и выводит массив, в котором каждый элемент является соответсвующим заголовком 
32 function decode_header($header) {
33  $headers explode("\r\n",$header);
34  $decodedheaders = Array();
35  foreach($headers as $header_item){
36   $thisheader trim($header_item);
37   if(!empty($thisheader))
38   {
39    if(!ereg("^[A-Z0-9a-z_-]+:",$thisheader))
40     $decodedheaders[$lasthead] .= " $thisheader";
41    else {
42     $dbpoint strpos($thisheader,":");
43     $headname strtolower(substr($thisheader,0,$dbpoint));
44     $headvalue trim(substr($thisheader,$dbpoint+1));
45     if($decodedheaders[$headname] != ""
46      $decodedheaders[$headname] .= "; $headvalue";
47     else 
48      $decodedheaders[$headname] = $headvalue;
49     $lasthead $headname;
50    }
51   }
52  }
53  return $decodedheaders;
54 }
55 
56 // перекодировщик тела письма. 
57 // Само письмо может быть закодировано и данная функция приводит тело письма в нормальный вид.
58 // Так же и вложенные файлы будут перекодироваться этой функцией. 
59 function compile_body($body,$enctype,$ctype) {
60  $enctype explode(" ",$enctype); $enctype $enctype[0];
61  if(strtolower($enctype) == "base64")
62  $body base64_decode($body);
63  elseif(strtolower($enctype) == "quoted-printable")
64  $body quoted_printable_decode($body);
65  if(ereg("koi8"$ctype)) $body convert_cyr_string($body"k""w");
66  return $body;
67 }
68 /* END FUNCTIONS BLOCK */

Основной код (email_upload.php)


Теперь, когда все готово, пришло время писать скрипт для получения писем и их обработки.
Для начала, создадим объект $pop3 и подсоединимся к серверу.
$user='username';
$pass='secure';
$host='mysite.com';
$port="110";

// Создание объекта
$pop3 =& new Net_POP3();

// Соединение с сервером
10 if(PEAR::isError$ret$pop3->connect($host $port ) )){
11   echo "ERROR: " $ret->getMessage() . "\n";
12   exit();
13 }
14 
15 // Авторизация на сервере
16 if(PEAR::isError$ret$pop3->login($user $pass,'USER' ) )){
17   echo "ERROR: " $ret->getMessage() . "\n";
18   exit();
19 }

Теперь все готово для получения списка писем и их обработки. Это и делаем
$message_list=$pop3->getListing(); // Получаем массив писем.


Пройдемся по всем письмам в цикле:
foreach($message_list as $message){
 $filenames[] = array();
 /* START GET PARSED HEADERS */
 // Получаем ИД текущено сообщения
 $message_id $message['msg_id'];
 // Парсим заголовки
 $headers $pop3->getParsedHeaders($message_id);

 $type $ctype $headers['Content-Type'];
10  $ctype split(";",$ctype);
11  $types split("/",$ctype[0]);
12  $maintype trim(strtolower($types[0])); 
13  $subtype trim(strtolower($types[1]));
14   
15  /* END GET PARSED HEADERS */
16  /* START CREATE FILE LOCATION AND SQL DATA */
17 
18  // Получили данные с заголовка
19  $from_user_info $headers['Delivered-To'];
20 
21  // Далее получаем нужные АйДишники
22  preg_match('/([a-zA-Z0-9]+)@mysite.com/'$from_user_info$matches);
23  $user_guid $matches[1];
24 
25  // На основании $user_guid получаем $user_id и $event_id из базы данных
26  // Путь куда поместим файл
27  $file_location "/path/to/upload";
28 
29  // Помещаем данные для SQL запроса
30  $table_data = array(
31   'user_id' => $user_id,
32   'event_id' => $event_id 
33  );
34 
35  /* END CREATE FILE LOCATION AND SQL DATA */
36 
37  /* START GET BODY */
38  // Получаем тело сообщения
39  $message_text htmlspecialchars($pop3->getBody($message_id));
40 
41  // Проверяем его тип (на содержание прикрепленных файлов)
42  if($maintype=="multipart" && ereg($subtype,"signed,mixed,related")) // Если есть
43  {
44   // получаем метку-разделитель частей письма 
45   $boundary=get_boundary($headers['Content-Type']);
46 
47   // на основе этого разделителя разбиваем письмо на части
48   $part split_parts($boundary,$message_text);
49 
50   //Ищем файлы
51   foreach($part as $part_item){
52    // разбиваем текущую часть на тело и заголовки   
53    $email fetch_structure($part_item);
54    $header $email["header"];
55    $body $email["body"];
56 
57    // разбираем заголовки на массив
58    $headers decode_header($header);
59    $ctype $headers["Content-Type"];
60    $cid $headers["content-id"];
61    $Actype split(";",$ctype);
62    $types split("/",$Actype[0]); 
63    $rctype strtolower($Actype[0]);
64 
65    // теперь проверяем, является ли эта часть прикрепленным файлом
66    $is_download = (ereg("name=",$headers["content-disposition"].$headers["content-type"]) || $headers["X-Attachment-Id"] != "" || $rctype == "message/rfc822");
67 
68    if($is_download) {
69     // Имя файла можно выдернуть из заголовков Content-Type или Content-Disposition
70     $cdisp $headers["content-disposition"];
71     $ctype $headers["content-type"]; 
72     $ctype2 explode(";",$ctype); 
73     $ctype2 $ctype2[0];
74     $Atype split("/",$ctype);
75     $Acdisp split(";",$cdisp);
76     $fname $Acdisp[1];
77     if(ereg("filename=\"(.*)\"",$fname,$regs))
78      $filename $regs[1];
79     if($filename == "" && ereg("name=(.*)",$ctype,$regs))
80      $filename $regs[1];
81     $filename ereg_replace("\"(.*)\"","\\1",$filename);
82     $filename ereg_replace(""(.*)"","\\1",$filename);
83 
84     //читаем файл в переменную.
85     $body compile_body($body,$headers["content-transfer-encoding"],$ctype);
86 
87     // Указываем КУДА записать файл
88     $filename $file_location.trim($filename);
89 
90     // Формируем список файлов для записи в базу данных
91     $filenames[] = $filename;
92     // Собственно сохраняем
93     $ft=fopen($filename,"wb");
94     fwrite($ft,$body); 
95     fclose($ft);
96    } 
97   }
98  }
99 
100  // НА основе $filenames[] и $table_data создаем запросы и выполняем их.
101  $query "INSERT INTO event_foto(event_id, user_id, image_name) VALUES ";
102  $total_fotos count($filenames);
103  $current 1;
104  foreach($filenames as $file){
105   $query .= "('$event_id','$user_id','$file')";
106   if($total_fotos>$current)
107    $query .= ", ";
108   $current++;
109  }
110  mysql_query($query);
111  //И не забываем удалить текущее письмо
112  $pop3->deleteMsg($message_id);
113 }

Конец


Собственно все. Модуль работает. Осталось только повесить этот файлик на CRON (например, каждый час).
Спасибо на внимание. Желаю удачи!

5
Просмотров: 959
     

Новости inPHP.org Новости inPHP.org
30.12.2011   С наступающим Новым Годом!
Команда inPHP.org поздравляет всех посетителей сайта проекта с наступающим Новым Годом! Вероятно, многие участники проекта заметили некоторое затишье в уходящем году. Поверьте, в следующем всё будет несколько иначе!
31.12.2010   С Новым Годом!
Уважаемые коллеги, коллектив проекта inphp.org искренне поздравляет вас с наступающим Новым Годом!
27.05.2010   Технические работы.
По техническим причинам возможны кратковременные перебои в работе сайта проекта в период с 27.05.2010 по 1.06.2010.
02.03.2010   Доступ участников к разработке тестирований.
В тестовом режиме запущен функционал, позволяющий участникам проекта разрабатывать собственные тестирования. Разработанные сообществом тестирования не имеют ограничений официальной аккредитации и в любое время доступны любому участнику.
 
Содействуют развитию Содействуют развитию
Участники проекта Участники проекта
devate
100
    devate devate
Юрий
Специализации: 0
key
100
    key key
Константин
Специализации: 0
artpetrov
20
    artpetrov artpetrov

Специализации: 0
elsoft
120
    elsoft elsoft

Специализации: 0
dummer
60
    dummer dummer

Специализации: 0
 
Статистика проекта Статистика проекта
Всего пользователей: 3648
Из них аккредитовано: 1468
Были сегодня: 2

УровеньПользователи
7742%
62376,5%
52075,7%
41945,3%
336510%
2401,1%
13509,6%
отсутствует218059,8%