LINUX.ORG.RU

[http, CGI] Обработка загружаемых файлов


0

1

Подскажите, пожалуйста, какой-нибудь нормальный способ (на баше, или С) обработки запроса post при загрузке файлов на сервер. Сейчас у меня файлы обрабатываются при помощи костыля на баше:

#!/bin/sh
echo -e "Content-type: text/html\n"
TMP_FILE="/var/www/tmp/T_m_P_file_That_comes_from_outside_$$"
cat > $TMP_FILE
File=`head -n 2 $TMP_FILE | grep "filename=" | sed -e "s/.*filename=\"\(.*\)\".*/\1/" `
Content=`head -n 3 $TMP_FILE | grep Content-Type | sed "s/Content-Type: //"`
echo "<html><head><script language=JavaScript>function notify(){parent.Loaded();}</script></head><body onLoad=\"notify();\">"
echo "Filename: $File; Content type: $Content</body></html>"
NLINES=`wc -l $TMP_FILE | awk '{print $1}'`
head -n $(( $NLINES - 1 )) $TMP_FILE | tail -n $(( NLINES - 5 )) > /var/www/tmp/Incoming/$File
rm -f $TMP_FILE
Приличных решений на С не могу найти. Можно даже на баше, лишь бы без дурацких конвееров head | tail...

☆☆☆☆☆

я здесь вижу обработку входного потока.

предлагаю заменить первую строчку на #!/usr/bin/env perl и далее по смыслу.

Если же хочется на баше, то можно сделать цикл while read line; do ... done; из которого выходить по условию `test -z $line`, после чего можно перенаправлять поток (оставшийся Content) в нужный файл. Имя файла, его тип и размер можно узнать в цикле анализируя $line. В том же цикле можно и echo сделать.

Отдельно по поводу TMP_FILE - для создания таких сущностей есть стандартная утилита mktemp:

TMP_FILE=`mktemp /tmp/.uploadXXXXX`

gorilych ★★
()
Ответ на: комментарий от gorilych

В принципе, цикл не намного лучше. Выход из цикла, кстати, надо будет делать не по test -z $line, а по проверке равенства текущей строки и самой первой строки заголовка. Но и в этом случае останется проблема лишнего \r, который добавляется в конец файла.

Может быть, есть какие-нибудь готовые сишные библиотеки для обработки таких данных? Я-то могу такую функцию написать, но опять-таки, получится страшный костыль.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

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

anonymous
()
Ответ на: комментарий от anonymous

Это кто же такой умный аноним? Ну подскажи, что же за молоток в этом случае можно использовать (только не надо мне рассказывать про пыхпых).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от arsi
$ sudo cpan Catalyst 
$ perldoc Catalyst::Request::Upload

Что-то не хочется из-за одной простецкой задачи изучать перл...

Ладно, будет свободное время, сделаю обработчик на С.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

как ты думаешь, умный человек, почему простецкую едакую задачу до сих пор на си не реализовали? конечно, может роль скриптовых языков немного преувеличенна, но на 99,9% серверов тебе не дадут скомпилировать свою программу. к чему бы это и что это за переносимость?

anonymous
()

Решение задачи

Появилось время, сделал нормальный обработчик:

//getfile.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "query.c"

char tmp_file[512], buf[16384], file[512], file_dest[1024], *str;
int f_tmp = 0, f_file = 0, tail_length = 0, fsize = 0;

void printerr(char *err, int errno){
	printf("<script language=JavaScript>function notify(){parent.Loaded(\"%s!\");}</script></head><body onLoad=\"notify();\">\n</body></html>\n", err);
	if(f_tmp > 0){
		close(f_tmp);
		unlink(tmp_file);
	}
	if(f_file > 0 && errno){
		close(f_file);
		unlink(file_dest);
	}
	exit(errno);	
}

int main(){
	int n, i;
	printf("Content-type: text/html\n\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=koi8-r\">\n<html><head>\n");
	sprintf(tmp_file, "/var/www/tmp/newfile_XXXXXX");
	mkstemp(tmp_file);
	if( (f_tmp = open(tmp_file, O_RDWR|O_CREAT)) == -1)
		printerr("can't write temporary file", 1);
	chmod(tmp_file, 00666);
	n = read(0, buf, 16384);
	if(!get_qs_param(buf, "filename", file, 512))
		printerr("can't determine filename", 2);
	str = file;
	str++; str[strlen(str)-2] = 0; // избавляемся от кавычек в имени файла
	sprintf(file_dest, "/var/www/tmp/Incoming/%s", str);
	str = buf;
	for(i=0; i<4; i++){ // пропускаем первые четыре строки (заголовок)
		str = strstr(str, "\r");
		if(!str) break;
		if(i == 0) tail_length = str - buf + 6; // длина хвоста
		str++;
	}
	if(!str)
		printerr("stream header damaged", 3);
	str++; // пропускаем \n
	i = n - (str - buf); // длина оставшихся данных
	write(f_tmp, str, i);
	while(n = read(0, buf, 16384))
		write(f_tmp, buf, n);
	fsize = lseek(f_tmp, -tail_length, SEEK_END); // получаем искомый размер файла
	lseek(f_tmp, 0, SEEK_SET);
	if( (f_file = open(file_dest, O_RDWR|O_CREAT)) == -1)
		printerr("can't save file", 4);
	chmod(file_dest, 00666);
	while(n = read(f_tmp, buf, 16384)){
		if(n > fsize) n = fsize;
		write(f_file, buf, n);
		fsize -= n;
	}
	printerr("", 0);
}
//query.c
#include <string.h>
char* get_qs(char* buf, int l){
	char *m, *qs = NULL;
	if((m = getenv("REQUEST_METHOD")) && strcasecmp(m, "POST") == 0)
		qs = fgets(buf, l, stdin);
	else if( (qs = getenv("QUERY_STRING")) )
		qs = strncpy(buf, qs, l);
	return qs;
}

int get_qs_param(char *qs, char *param, char *meaning, int l){
	char *tok, *val, *par, str[l+1];
	int stat = 0;
	strncpy(str, qs, l);
	tok = strtok(str, "&,; |\n");
	do{
		if((val = strchr(tok, '=')) == NULL) continue;
		*val++ = '\0';
		par = tok;
		if(strcasecmp(par, param)==0){
			stat = 1;
			strncpy(meaning, val, l);
			break;
		}
	}while((tok = strtok(NULL, "&,; |\n"))!=NULL);
	return stat;
}

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: Решение задачи от Eddy_Em

а если разные юзеры одновременно заливают 2 файла? может быть во имени временного файла использовать псевдослучайное значение или pid?

linuks ★★★★★
()
Ответ на: комментарий от linuks

Так mkstemp и генерирует случайную 6значную последовательность (вместо ХХХХХХ), так что вероятность коллизий очень мала.

Eddy_Em ☆☆☆☆☆
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.