автор: Ruzanov Igor
постановка задачи
Сделать в приложении под Flash Player 6 и 7 возможность загрузки файлов на сервер. Плюс нужно во флэше обработать событие загрузки файла и в случае успеха получить имя файла, который выбрал пользователь. И сделать это так, что бы этот способ поддерживался как можно большем количеством браузеров.
описание проблемы
Основная трудность - вызвать стандартный файловый диалог браузера. Обычными способами flash+js этого сделать нельзя (вернее мне не удалось найти :) и нужно как-то выкручиваться.
методы решения
a) Первый вариант решения - это размещение во фрейме с нулевой шириной формы с одним элементом input типа file:
<form name="fileform" action="up.php" method="post" enctype="multipart/form-data" id="fileform">
<input type="file" name="filename" onChange="sendFile()">
</form>
1
2
3
4
// код JavaScript функции sendFile
function sendFile() {
fileform.submit();
}
А из флэша вызывать JavaScript функцию (подробно методы вызова JavaScript функций из флэш
описаны тут), которая будет программно вызывать метод click() файлового input'a в нашей форме и после выбора файла пользователем форма отправляет данные на сервер. Но, к сожалению, этот фокус пройдёт только в Internet Explorer поседних версий. Другие браузеры программный вызов метода click() не поддерживают.
Исходник варианта а) можно скачать здесь
б) Второй вариант решения - это открытие JavaScript'ом нового окна с обычной html формой, в которой пользователь выбирает файл и загружает его на сервер.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// JavaScript:
function openWin(url) {
var myWin;
if (!myWin || myWin.closed) {
myWin = window.open(url,
"blank",
"width=400,height=200,"+
"toolbar=0,location=0,"+
"directories=0,status=0,"+
"menubar=0,scrollbars=0,"+
"resizable=0,"+
"top="+((screen.height/2)-100)+","+
"left="+((screen.width/2)-200));
} else {
myWin.focus();
}
}
Данная функция вызывается из флэша (например, через getURL) и если новое окно уже открыто, то наводим на него фокус или, в противном случае, создаём его. Для красоты располагаем его по центру экрана, вычисляя координаты top и left.
В открываемом новом окне будет находиться простая форма:
<form enctype="multipart/form-data" method="post" name="form" action="uphtml.php">
<input type="file" name="filename" id="filename">
<input type="submit" value="Upload">
</form>
В качестве серверной стороны будем использовать php скрипт. Это скрипт должен в случае успешной загрузки файла сохранить его в нужном месте и вернуть флэшу имя файла, а если загрузка прошла не удачно, то вернуть 0. Самый простой способ - это из нового окна вызвать метод SetVariable для флэш объекта (в новом окне можно получить доступ к окну, которое его открыло, через ссылку window.opener).
PHP скрипт генерирует html страницу, в которой JavaScript функция передаёт флешу результат загрузки:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if($_FILES["filenam"]["error"] != UPLOAD_ERR_OK) {
$res = 0;
} else {
$file = $_FILES["filename"]["name"];
@rename($_FILES["filename"]["tmp_name"],$file);
$res = $file;
}
$page = "<script language='JavaScript'>";
$page .= "function send2Flash() {";
$page .= "window.opener.document.index.SetVariable('res','".$res."');"
$page .= "}";
$page .= "</script></head>";
$page .= "<body onLoad='send2Flash();window.close();'";
$page .= "></body></html>";
print($page);
где index - имя(id) флэш объекта.
Во флэше можно отреагировать на установку нового значения переменной res JavaScript'ом и вызвать функцию:
1
2
3
4
5
6
var res = null;
_root.watch("res",jsReturn);
function jsReturn() {
// обработчик события - изменение res
// новое значение res находиться в arguments[2]
}
Но, к сожалению, метод SetVariable нигде, кроме как в Mozilla(pc), Internet Explorer 5/6(pc), Firefox(pc) не работает. Тогда придется слегка изменить этот вариант и вместо SetVariable передавать данных флэшу при помощи прокси-флэшки и LocalConnection. (подробнее о таком виде коммуникации JS - Flash
рассказано здесь.)
в) Окончательный вариант.
Всё остаётся без изменений, как и во втором варианте до момента передачи данных флэшу. Только для удобства форма и обработчик формы будут находиться в одном php файле. Плюс, поскольку url-адрес нового окна формируется на флэшовой стороне, то мы можем передать этому скрипту методом get какие-то данные из флэшки:
1
2
3
4
function openWin(url) {
getURL("javascript:openWin('"+url+"');");
};
openWin("upwin.php?data=test");
Изменится только метод передачи данных флэшу.
Принцип метода: мы создаем на странице <div>, содержащий прокси-флэшку. Задача этой флэшки – принять те переменные, которые переданы ей через FlashVars и передать их при помощи LocalConnection в главную флэшку. Когда нам необходимо передать новые переменные, мы переписываем код тегов <object> и <embed> прокси-флэшки при помощи JavaScript, чем вызываем её перезагрузку. Таким образом, мы используем для передачи флэшовый механизм LocalConnection, а со стороны javascript используем только передачу переменных через FlashVars (что поддерживается всеми современными броузерами).
Теперь код функции send2Flash, генерируемый php на новой странице, будет иметь вид:
1
2
3
$page .= "function send2Flash() {";
$page .= window.opener.setFlashVariable(\"res\",\"".$res."\");
$page .= "}";
Функция setFlashVariable находится на главной странице и переписывает код прокси-флэшки, передавая ей через FlashVars переменную:
1
2
3
4
function setFlashVariable (name, value) {
var refresh = document.getElementById("refresh");
refresh.innerHTML = refresh_html.split("$$$$").join(name+"="+value);
}
Код для прокси-флэшки, которая будет регулярно перезагружаться и передавать сигналы главной флэшке:
1
2
3
4
5
6
7
8
9
10
11
12
13
var refresh_html =
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" '+
'codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/'+
'flash/swflash.cab#version=7,0,0,0" '+
'width="1" height="1" >'+
'<param name="movie" value="refresh.swf" />'+
'<param name="FlashVars" value="lcid='+connection_id+'&$$$$" />'+
'<embed src="refresh.swf" '+
'FlashVars="lcid='+connection_id+'&$$$$" '+
'width="1" height="1" '+
'type="application/x-shockwave-flash" '+
'pluginspage="http://www.macromedia.com/go/getflashplayer" />'+
'</object>';
Внутри прокси-флэшки не должно быть ничего, кроме следующего кода:
1
2
3
var lc_name = "lc_"+lcid;
var js_lc = new LocalConnection();
js_lc.send(lc_name, "uploadCallBack", res);
В главной флэшке:
1
2
3
4
5
6
var lc_name = "lc_"+lcid;
var js_lc = new LocalConnection();
js_lc.connect(lc_name);
js_lc.uploadCallBack = function (res) {
// обработчик
}
Исходник окончательного варианта можно скачать тут.
вывод
Последний вариант должен работать в большинстве браузерах, даже в таких "проблематичных" как Opera и браузеры под Mac.
Тестировалось на IE 6, Firefox 1.5 и Opera 7.53