Архив за Июль 2008

Создание экрана ожидания

Одним из самых существенных ограничений современных web-служб является время ожидания, когда нечто происходит, но в фоновом режиме, о чем следует непременно уведомить пользователей, иначе они не обратят внимания на происходящее. Для этой цели можно изменить вид курсора (cursor:wait) или же вывести надпись “ожидание” или “загрузка”, например при обработке вызова XMLHttpRequest. Web-cайт Google Mail стал одним из первых, где был использован данный прием.

При отправке асинхронного запроса на сервер в правом верхнем углу появляется экран загрузки (впрочем, его можно расположить в каком угодно месте). Как только от сервера возвратятся данные, экран загрузки становится невидимым.

Файл loading.html

<html>
<head>
<title>Loading...</title>
<script language="JavaScript" type="text/javascript" src="ajax.js"> </script>
</head>
<body>
<input type="button" value="Загрузить" onclick="loadData();" />
<span id="loading"
style="position: absolute; right:0; top:0; visibility: hidden; color: red;">
Загрузка...</span>
</body>
</html>

Файл ajax.js

var XMLHttp = getXMLHttp();

function loadData() {
  XMLHttp.open("GET", "delay.php", true);
  XMLHttp.onreadystatechange = handlerFunction;
  XMLHttp.send(null);
  document.getElementById("loading").style.visibility = "visible";
}

function handlerFunction() {
  if (XMLHttp.readyState == 4) {
    document.getElementById("loading").style.visibility = "hidden";
    window.alert("Ответ сервера: " + XMLHttp.responseText);
  }
}

function getXMLHttp() {
  if (window.XMLHttpRequest) {
    try {
      XMLHttp = new XMLHttpRequest();
    } catch (e) { }
  } else if (window.ActiveXObject) {
    try {
      XMLHttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        XMLHttp = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) { }
    }
  }
  return XMLHttp;
}

Серверный скрипт delay.php

<?php
  sleep(10);
  echo 'результат запроса';
?>

Вместо надписи “Загрузка…” можно использовать какую-нибудь анимированную картинку:

Красивый индикатор загрузки можно загрузить с сайта http://www.ajaxload.info/:

   

Sypex Dumper — быстрый и удобный бекап

Наткнулся в Сети на очень интересный скрипт для резервного копирования базы данных - ниже приводится его описание с официального сайта.

Изначально Sypex Dumper создавался для работы с большими базами данных (сотня мегабайт для него не проблема), при этом он должен был быть максимально быстрым, компактным и удобным.

Основные возможности

  • создание резервной копии и восстановление базы данных MySQL без использования сторонних программ, таких как mysqldump;
  • работа с базами данных любых размеров (от нескольких килобайт до сотен мегабайт), в связи с этим вся работа с файлами бекапа осуществляется по FTP, но download возможен и с помощью менеджера загрузки (Download Master, Reget и др.);
  • поддержка двух форматов сжатия файлов (Gzip и Bzip2), а также разной степени сжатия;
  • поддержка фильтров для таблиц, с их помощью легко можно выбрать нужные таблицы;
  • работает на Windows и Linux;
  • высокая скорость работы (в 2 раза быстрее, чем phpMyAdmin);
  • оригинальный алгоритм парсинга дампов благодаря которому дампер обгоняет даже некоторые windows-приложения, не говоря уже о скриптах (в 4 раза быстрее, чем phpMyAdmin);
  • индикатор выполнения задачи, благодаря чему всегда видно на каком этапе работы находится срипт;
  • очень компактный (всего 25 КБ);
  • легко настраивается;
  • сохраняются последние настройки, что удобно при частом использовании;
  • имеется собственная система авторизации, несколько вариантов авторизации, в том числе пользовательская;
  • небольшой расход памяти при работе;
  • совместимость с дампами phpMyAdmin и mysqldump;

История создания

Началась, наша история, когда база данных одного сайта всё сильнее разрасталась, и размеры её перевалили за 10 МБ, а глубокоуважаемый phpMyAdmin вместо столь желанного дампа начал выдавать пустые страницы либо ошибки. Естественно phpMyAdmin многократно мысленно проклинался, но делу это не помогло. Тогда начались поиски альтернативного софта, но ничего достойного внимания найдено не было.

Что делать? Пришлось заняться созданием своей утилиты для резервного копирования и восстановления базы данных MySQL. А далее дело техники…

Через некоторое время утилита была готова, и оставалось только обкатать её. В течение нескольких месяцев эксплуатации дампер отлично проявил себя, в нем были устранены некоторые ошибки и увеличено быстродействие. Спустя некоторое время было принято решение сделать эту полезную утилиту общедоступной.

Как отдать пользователю файл скриптом

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

Файл download.php

<?php
if(isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))
  header('Content-Type: application/force-download');
else
  header('Content-Type: application/octet-stream');
header('Accept-Ranges: bytes');
header('Content-disposition: attachment; filename="pricelist.zip"');
?>

Тогда пользователь зашедший на download.php предстанет перед выбором, что делать с файлом pricelist.zip: сохранить на диск, открыть или отменить скачивание. Заголовок

Content-Type: application/force-download

нужен для старых версий MS IE.

С поддержкой докачки:

<?php

$filename = 'pricelist.zip';
 
// если файла нет
if (!file_exists($filename)) {
  header ("HTTP/1.0 404 Not Found");
  exit;
}
 
// получим размер файла
$fsize = filesize($filename);
// дата модификации файла для кеширования
$ftime = date("D, d M Y H:i:s T", filemtime($filename));
// смещение от начала файла
$range = 0;
 
// пробуем открыть
$handle = @fopen($filename, "rb");

// если не удалось
if (!$handle){
  header ("HTTP/1.0 403 Forbidden");
  exit;
}
 
// Если запрашивающий агент поддерживает докачку
if ($_SERVER["HTTP_RANGE"]) {
  $range = $_SERVER["HTTP_RANGE"];
  $range = str_replace("bytes=", "", $range);
  $range = str_replace("-", "", $range);
  // смещаемся по файлу на нужное смещение
  if ($range) fseek($handle, $range);
}
 
// если есть смещение
if ($range) {
  header("HTTP/1.1 206 Partial Content");
} else {
  header("HTTP/1.1 200 OK");
}
 
header("Content-Disposition: attachment; filename=\"{$filename}\"");
header("Last-Modified: {$ftime}");
header("Content-Length: ".($fsize-$range));
header("Accept-Ranges: bytes");
header("Content-Range: bytes {$range}-".($fsize - 1)."/".$fsize);
 
// подправляем под IE что б не умничал
if(isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'],'MSIE'))
  Header('Content-Type: application/force-download');
else
  Header('Content-Type: application/octet-stream');
 
while(!feof($handle)) {
  $buf = fread($handle,512);
  print($buf);
}
 
fclose($handle);
 
?>

Чтобы получить от сервера не весь документ, а только оставшуюся часть, клиент должен отправить заголовок

Range: bytes=num-

где num - смещение в байтах от начала файла.

Сервер в свою очередь устанавливает переменную окружения HTTP_RANGE и должен отправить заголовок

HTTP/1.1 206 Partial Content

который дает клиенту понять, что отдается часть контента. Далее сервер должен отдать клиенту ту часть контента, которую тот запрашивал с соответствующими заголовками:

Content-Type: application/octet-stream
Content-Disposition: attachment; filename=имя_файла
Last-Modified: время_модификации_файла
Accept-Ranges: bytes
Content-Length: длина_отдаваемой_части
Content-Range: от-до/размер

Поясню последний заголовок на примере: имеем файл размером 4000 байт, отдаем кусок с 5 по 288 байт. Тогда заголовок будет выглядеть так:

Content-Range: 5-288/4000

В общем случае, протокол HTTP 1.1 позволяет клиенту запросить только часть объекта (от и до). Если клиент посылает заголовок

Range-Unit: 2015 | 1024

это означает, что клиент хочет получить кусок документа, начиная с 2015 байта и длиной в 1 килобайт. Если сервер поддерживает докачку и документ не является динамическим, то будет выдана запрашиваемая часть. В противном случае сервер вернёт ошибку о том, что действие не поддерживается или начнёт выдавать документ полностью.