Что нового

[Сеть, интернет] Менеджер закачек + многопоточность + докачка.

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Вот посетила меня безумная идея сделать вот такой вот менеджер закачек... т.к. я не любитель выкладывать исходники своих полноценно доделаных программ - буду выкладывать свои наработки, авось кому и сгодятся...

Код:
$link = InputBox("Insert link",'Вставьте сюда ссылку на файл','')
$fSize = InetGetSize ($link)
If $fSize = 0 Then
	MsgBox (0,'Error','Нету такого файла.')
	Exit
EndIf

$file_path = FileSaveDialog ( "", "::{450D8FBA-AD25-11D0-98A8-0800361B1103}", "All (*.*)")
$file = FileOpen ($file_path,10)
$req_max = InputBox("Потоки",'Сколько одновременно использовать потоков?','5')

$req_all = Ceiling($fSize/10000)
If $req_all > $req_max Then
	$req_sets = Ceiling($req_all/$req_max)
Else
	$req_sets = 1
EndIf

Dim $reqS[$req_sets*$req_max+1]
$reqS[0]=$req_all
Dim $tempS[$req_sets*$req_max+1]
$tempS[0]=$req_all


For $req_sets_cur = 1 To $req_sets
	For $i = $req_sets_cur*$req_max-$req_max+1 To $req_sets_cur*$req_max
		If $i > $req_all Then ExitLoop
		$reqS[$i] = _request($i)
	Next

	For $i = $req_sets_cur*$req_max-$req_max+1 To $req_sets_cur*$req_max
		If $i > $req_all Then ExitLoop
		$reqS[$i].WaitForResponse
		FileWrite ($file,$reqS[$i].ResponseBody)
		TrayTip ('Закачка',"Загружено "&Round(FileGetSize($file_path)/1024)&"Кб из "&Round($fSize/1024)&"Кб",200)
	Next
Next
FileClose ($file)


Func _request($num)
	$host = StringFindSE ($link,'http://','/',1,1)
	$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
	$oHTTP.Open("GET", $link)
	$oHTTP.SetRequestHeader("Host",$host)
	$oHTTP.SetRequestHeader("Accept", "*/*")
	$oHTTP.SetRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)")
	$oHTTP.SetRequestHeader("Range", "bytes="&$num*10000-10000&"-"&$num*10000-1)
	$oHTTP.SetRequestHeader("Referer",'http://'&$host&'/')
	$oHTTP.setTimeouts(5000, 5000, 15000, 15000)
	$oHTTP.Send("")
	If @error Then Exit
	ConsoleWrite("-=-=-= Request "&$num&" =-=-=-"&@CRLF)
	ConsoleWrite($oHTTP.Status & "   " & $oHTTP.StatusText&@CRLF)
	Return $oHTTP
EndFunc

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc


Система пока примитивная очень, даже многопоточностью назвать сложно, в общем алгоритм такой:
1. Проверяется размер файла, делиться на куски по 10 000 байт.
2. Разбивается на группы по 5 кусков.
3. Отправляется 5 запросов на закачку первых 5-ти частей.
4. Дожидается пока будут закачаны все первые 5 частей, по очереди каждую часть.
5. Записывает части по мере загрузки в файл.
6. Отсылает 5 запросов на закачку следующих 5-ти частей.
7. Дожидается пока будут закачаны эти 5 частей.
....(и т.д. по циклу)
N. Закрывает файл

Вот такой скромный алгоритм... Если есть интерес у окружающих - буду выкладывать по мере дорабатываемости... Если интереса нету - значит не судьба :smile:

ЗЫ: курсивом выделил то, над чем собираюсь работать =)


ДОПОЛНЕНИЕ: Было выявлено, что данный код не рабочий. (подробнее в сообщениях 6 и 7)

И так, раз мы многопоточности так и не добились - то пока что предлагаю подумать о докачке... А точнее сказать я о ней уже подумал ;)
Вот вам пример докачивающего скрипта, при запуске указываете прямую ссылку на файл - начинается простая загрузка (обычным InetGet).
Если прервать работу скрипта (например Ctrl+Break в SciTE) и запустить скрипт заново, указав ту же ссылку и тот же файл (не докачанный) то выскочит мес-бокс, с вопросом "Докачать?", жмете да и файл докачивается (подробнее в комменте скрипта).

Код:
;~   Докачка осуществляеться по-пакетно, т.е. по частям,
;~  для того чтобы в промежутке между частями выводить
;~  процент закачки и количество скачанных байт.
;~   Если значение $pack установить равное нулю - то докачка
;~  будет происходить одним пакетом, скачиваемый файл будет
;~  харниться в паняти до завершения закачки, после чего будет
;~  сохранен на диск.
;~   Выставлять значение равное нулю не рекомендуеться,
;~  т.к. при закачке боьших файлов будет забиваться память,
;~  а при обрыве придется докачивать заново с того же места,
;~  с которого начинали докачивать ранее.
;~   Но и слишком маленький объем пакета ставить тоже не рекомендуется,
;~  т.к. на запрос части файла тоже тратиться время и трафик.
;~  Чем больше запросов - тем больше трафика сожранного в пустую
;~  и больше затраченного времени...
;~   Самый опимальный (по моему мнению) пакет - 50 Кб (51 200 байт).
Global $pack = 51200   ;Это пакет при докачке (в байтах)

While 1
Global $link = InputBox("Insert link",'Вставьте сюда ссылку на файл','')
Global $fSize = InetGetSize ($link)
Global $filename = StringFindSE($link,'/','',-1,-1)
Global $filetype = StringFindSE($filename,'.','',-1,-1)
If $fSize > 0 Then
	ExitLoop
EndIf
MsgBox (16,'Error','Файл не найден.')
WEnd
Global $file_path = FileSaveDialog ("Куда сохранять?", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "(*."&$filetype&")",0,$filename)
Global $filenameN = StringFindSE($file_path,'\','',-1,-1)
If FileExists ($file_path) And FileGetSize($file_path) > 0 Then
	$do = MsgBox (35,"Докачать?","Докачать файл?"&@CRLF&@CRLF&"Если нажать ''Нет'', то файл будет заменен.")
	If $do = 2 Then
		Exit
	ElseIf $do = 6 Then
		Global $file = FileOpen ($file_path,9)
		$ffSize = FileGetSize($file_path)
		_resume($ffSize)
	ElseIf $do = 7 Then
		FileDelete ($file_path)
		_download ()
	EndIf
ElseIf Not FileExists ($file_path) Then
	_download()
Else
	FileDelete ($file_path)
	_download ()
EndIf
MsgBox (0,'Готово',"Файл "&$filenameN&" закачан!")
Exit



Func _download()
$download = InetGet($link,$file_path,1,1)
ProgressOn("Закачка файла "&$filenameN, "Файл "&$filenameN&" закачивается...", "0 Кб из "&Round($fSize/1024)&" Кб")
While InetGetInfo($download,2) = False
	$aData = InetGetInfo($download,0)
	ProgressSet(Round($aData/$fSize*100), "Файл "&$filenameN&" закачивается... "&Round($aData/$fSize*100)&"%", Round($aData/1024)&" Кб из "&Round($fSize/1024)&" Кб")
	Sleep(250)
Wend
ProgressOff ()
InetClose ($download)
EndFunc

Func _resume($StartByte)
	$i = 1
	If $pack > 0 Then
		ProgressOn("Закачка файла "&$filenameN, "Файл "&$filenameN&" закачивается..."&Round($ffSize/$fSize*100)&"%", Round($ffSize/1024)&" Кб из "&Round($fSize/1024)&" Кб")
		For $StartByte = $StartByte To $fSize Step $pack
			ConsoleWrite(@CRLF&"Запрос №"&$i&@CRLF&"Стартовый байт запроса = "&$StartByte&@CRLF&"Конечный байт запроса = "&$StartByte+$pack-1&@CRLF&"Всего байт в файле = "&$fSize&@CRLF)
			_request($StartByte,$StartByte+$pack-1)
			ProgressSet(Round(($StartByte+$pack-1)/$fSize*100), "Файл "&$filenameN&" закачивается... "&Round(($StartByte+$pack-1)/$fSize*100)&"%", Round(($StartByte+$pack-1)/1024)&" Кб из "&Round($fSize/1024)&" Кб")
			$i += 1
		Next
		ProgressOff()
	Else
		TrayTip ("Внимание","Файл закачиваеться в фоновом режиме, ни каких уведомлений о состояянии загрузки не будет выводиться до завершения загрузки.",5000,1)
		_request($StartByte,'')
	EndIf
EndFunc

Func _request($SB,$EB)
	$host = StringFindSE ($link,'http://','/',1,1)
	$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
	$oHTTP.Open("GET", $link)
	$oHTTP.SetRequestHeader("Host",$host)
	$oHTTP.SetRequestHeader("Accept", "*/*")
	$oHTTP.SetRequestHeader("User-Agent", "Downloader/1.0 (compatible)")
	$oHTTP.SetRequestHeader("Range", "bytes="&$SB&"-"&$EB)
	$oHTTP.SetRequestHeader("Referer",'http://'&$host&'/')
	$oHTTP.setTimeouts(5000, 5000, 15000, 15000)
	$oHTTP.Send("")
	ConsoleWrite($oHTTP.Status & "/" & $oHTTP.StatusText&@CRLF)
	$oHTTP.WaitForResponse
	FileWrite ($file,$oHTTP.ResponseBody)
EndFunc

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc
 

sforce5

Олл фо ЛулзЪ
Сообщения
160
Репутация
41
Ащипка стр. 35

Код:
TrayTip ('Закачка',"Загружено "&Round(($num*10000-10000)/1024)&"Кб из "&Round($fSize/1024),200)


что за $num?
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Уже исправил, извиняюсь =)) Код не до конца отредактировал и сюда скопирил, потом только заметил =))
 

sforce5

Олл фо ЛулзЪ
Сообщения
160
Репутация
41
А теперь фэйл на 51 стр.

Код:
$oHTTP.Send("")


Лучше во вторую строку вставить:
Код:
If @error Then Exit
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Во вторую, в смысле после $oHTTP.Send("") ? Если да - то вставил =))
Обработку возможных ошибок не делал, это пока наброски только :smile:
 

sforce5

Олл фо ЛулзЪ
Сообщения
160
Репутация
41
HukpoFuJl сказал(а):
Во вторую, в смысле после $oHTTP.Send("") ? Если да - то вставил =))
Обработку возможных ошибок не делал, это пока наброски только :smile:

Нет, во вторую строку кода, а не после обьекта, про обьект я имел ввиду что на нём ошибку выдаёт
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Походу облом, никакой многопоточности...
Код:
#Include <Array.au3>


$link = InputBox("Insert link",'Вставьте сюда ссылку на файл','')
$fSize = InetGetSize ($link)
$filename = StringFindSE($link,'/','',-1,-1)
$filetype = StringFindSE($filename,'.','',-1,-1)

If $fSize = 0 Then
	MsgBox (0,'Error','Файл не найден.')
	Exit
EndIf

$file_path = FileSaveDialog ("Куда сохранять?", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "(*."&$filetype&")",0,$filename)
$file = FileOpen ($file_path,10)
$req_max = InputBox("Потоки",'Сколько одновременно использовать потоков?','5')
$pack = InputBox("Объем пакета",'Какого объема брать куски для закачки? (Кб)','20')*1024

$req_all = Ceiling($fSize/$pack)
If $req_all > $req_max Then
	$req_sets = Ceiling($req_all/$req_max)
Else
	$req_sets = 1
EndIf

Dim $reqS[$req_sets*$req_max+1]
$reqS[0]=$req_all
Dim $tempS[$req_sets*$req_max+1]
$tempS[0]=$req_all


For $req_sets_cur = 1 To $req_sets
		ConsoleWrite("Start send requests: ")
	For $i = $req_sets_cur*$req_max-$req_max+1 To $req_sets_cur*$req_max
		If $i > $req_all Then ExitLoop
		$reqS[$i] = _request($i)
	Next
		ConsoleWrite("     <--- OK"&@CRLF)
	For $i = $req_sets_cur*$req_max-$req_max+1 To $req_sets_cur*$req_max
		If $i > $req_all Then ExitLoop
		ConsoleWrite("-=-=-= Request "&$i&" =-=-=-"&@CRLF)
		$reqS[$i].WaitForResponse
		ConsoleWrite($reqS[$i].Status & "/" & $reqS[$i].StatusText&@CRLF)
		FileWrite ($file,$reqS[$i].ResponseBody)
		TrayTip ('Закачка',"Загружено "&Round(($i*$pack-$pack)/1024)&"Кб из "&Round($fSize/1024)&"Кб",200)
	Next
Next
FileClose ($file)


Func _request($num)
	$host = StringFindSE ($link,'http://','/',1,1)
	$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
	$oHTTP.Open("GET", $link)
	$oHTTP.SetRequestHeader("Host",$host)
	$oHTTP.SetRequestHeader("Accept", "*/*")
	$oHTTP.SetRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)")
	$oHTTP.SetRequestHeader("Range", "bytes="&$num*$pack-$pack&"-"&$num*$pack-1)
	$oHTTP.SetRequestHeader("Referer",'http://'&$host&'/')
	$oHTTP.setTimeouts(5000, 5000, 15000, 15000)
	$oHTTP.Send("")
	ConsoleWrite ('<'&$num&'> ')
	Return $oHTTP
EndFunc

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc

Сделал чтоб можно было выбирать объем пакета... Ставлю на закачку большой файлик, ставлю большой объем пакетов и ставлю в цикле сразу после Send("") запись в консоль... И вижу как запись в консоли появляеться... В общем Send("") отправляет запрос И ЖДЕТ ОТВЕТА :'( вот те и менеджер закачек...

Предложите что-нибудь альтернативное WinHttp.WinHttpRequest.5.1 !?
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Так, скажу конкретнее... нужно реализовать тоже самое с помощью библиотеки WinHTTP.au3

Мой текущий код выглядит просто:

Код:
$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
$oHTTP.Open("GET", "http://autoit-script.3dn.ru/_ld/0/56024970.jpg")
$oHTTP.SetRequestHeader("Host", "autoit-script.3dn.ru")
$oHTTP.SetRequestHeader("Accept", "*/*")
$oHTTP.SetRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)")
$oHTTP.SetRequestHeader("Range", "bytes=0-104857600")
$oHTTP.SetRequestHeader("Referer","autoit-script.3dn.ru")
$oHTTP.setTimeouts(5000, 5000, 15000, 15000)
$oHTTP.Send("")
$oHTTP.WaitForResponse
;$status = $oHTTP.Status ;не обязательно
;$status_text = $oHTTP.StatusText ;не обязательно
FileWrite ("Temp.jpg",$oHTTP.ResponseBody)


Мне нужен аналогичный скрипт, только с использованием библиотеки WinHTTP.au3
Ну или аналогия с использованием TCPSend/TCPRecv.

Если будете что-либо предлагать - заранее проверьте скачивается ли картинка нормально...
А то я тут уже попробовал написать скрипт при помощи WinHTTP.au3 и получил "Temp.jpg" 5,51 Кб вместо 263 Кб =)))
Код:
#include <WinHTTP.au3>
$hOpen = _WinHttpOpen()
$hConnect = _WinHttpConnect($hOpen, "autoit-script.3dn.ru")
$hRequest = _WinHttpOpenRequest($hConnect, "GET", "/_ld/0/56024970.jpg")

_WinHttpSendRequest($hRequest,	"Host: autoit-script.3dn.ru"&@CRLF& _
								"Accept: */*"&@CRLF& _
								"User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)"&@CRLF& _
								"Range: bytes=0-104857600"&@CRLF& _
								"Referer: http://autoit-script.3dn.ru/")
_WinHttpReceiveResponse($hRequest)
$header = _WinHttpQueryHeaders($hRequest)
Local $data=""
Do
	$data&=_WinHttpReadData($hRequest)
Until @error<>0
FileWrite ("Temp.jpg",$data)
 

Rjevsky

Новичок
Сообщения
102
Репутация
4
По поводу скачивания по сегментам, какой в этом смысл? Забивание канала бесполезными потоками? И палка в колёса ещё не реализованной, но реально нужной функции - докачки.

Год назад, мне требовался FTP клиент с функцией докачки. Т.к. никакими языками программирования не владею, но Autoit как то дался легко (не без помощи форумчан), то искал способы осуществления докачки средствами autoit. Авторы FTP udf так же не радовали возможностью такой функции. Но выход был мною найден: использование ActiveX компонентов, с которыми autoit прекрасно дружит и имеет ряд преимуществ.

собственно вот тема. Пример весьма работоспособный. Доведённый полностью до ума прекрасно работает уже более года и выполняет все те функции, о которых я даже и не мечтал перед тем, как взялся за написание. А вот с докачкой по http даже и не знаю как обстоят дела и как это можно реализовать. Надеюсь мой пример чем то поможет.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Rjevsky
Вы не могли бы перезалить свой пример с FTP, а то файл удален.
 

Rjevsky

Новичок
Сообщения
102
Репутация
4
Именно того примера у меня не сохранилось. Нашёл только какой то рабочий проект. Файлом install нужно зарегестрировать dll.
По ini файлу разберётесь с настройками сервера. Клиент пытается после скачивания удалять файлы, поэтому будьте внимательны.
http://slil.ru/29521893
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
И так, раз мы многопоточности так и не добились - то пока что предлагаю подумать о докачке... А точнее сказать я о ней уже подумал ;)
Вот вам пример докачивающего скрипта, при запуске указываете прямую ссылку на файл - начинается простая загрузка (обычным InetGet).
Если прервать работу скрипта (например Ctrl+Break в SciTE) и запустить скрипт заново, указав ту же ссылку и тот же файл (не докачанный) то выскочит мес-бокс, с вопросом "Докачать?", жмете да и файл докачивается (подробнее в комменте скрипта).

Код:
;~   Докачка осуществляеться по-пакетно, т.е. по частям,
;~  для того чтобы в промежутке между частями выводить
;~  процент закачки и количество скачанных байт.
;~   Если значение $pack установить равное нулю - то докачка
;~  будет происходить одним пакетом, скачиваемый файл будет
;~  харниться в паняти до завершения закачки, после чего будет
;~  сохранен на диск.
;~   Выставлять значение равное нулю не рекомендуеться,
;~  т.к. при закачке боьших файлов будет забиваться память,
;~  а при обрыве придется докачивать заново с того же места,
;~  с которого начинали докачивать ранее.
;~   Но и слишком маленький объем пакета ставить тоже не рекомендуется,
;~  т.к. на запрос части файла тоже тратиться время и трафик.
;~  Чем больше запросов - тем больше трафика сожранного в пустую
;~  и больше затраченного времени...
;~   Самый опимальный (по моему мнению) пакет - 50 Кб (51 200 байт).
Global $pack = 51200   ;Это пакет при докачке (в байтах)

While 1
Global $link = InputBox("Insert link",'Вставьте сюда ссылку на файл','')
Global $fSize = InetGetSize ($link)
Global $filename = StringFindSE($link,'/','',-1,-1)
Global $filetype = StringFindSE($filename,'.','',-1,-1)
If $fSize > 0 Then
	ExitLoop
EndIf
MsgBox (16,'Error','Файл не найден.')
WEnd
Global $file_path = FileSaveDialog ("Куда сохранять?", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "(*."&$filetype&")",0,$filename)
Global $filenameN = StringFindSE($file_path,'\','',-1,-1)
If FileExists ($file_path) And FileGetSize($file_path) > 0 Then
	$do = MsgBox (35,"Докачать?","Докачать файл?"&@CRLF&@CRLF&"Если нажать ''Нет'', то файл будет заменен.")
	If $do = 2 Then
		Exit
	ElseIf $do = 6 Then
		Global $file = FileOpen ($file_path,9)
		$ffSize = FileGetSize($file_path)
		_resume($ffSize)
	ElseIf $do = 7 Then
		FileDelete ($file_path)
		_download ()
	EndIf
ElseIf Not FileExists ($file_path) Then
	_download()
Else
	FileDelete ($file_path)
	_download ()
EndIf
MsgBox (0,'Готово',"Файл "&$filenameN&" закачан!")
Exit



Func _download()
$download = InetGet($link,$file_path,1,1)
ProgressOn("Закачка файла "&$filenameN, "Файл "&$filenameN&" закачивается...", "0 Кб из "&Round($fSize/1024)&" Кб")
While InetGetInfo($download,2) = False
	$aData = InetGetInfo($download,0)
	ProgressSet(Round($aData/$fSize*100), "Файл "&$filenameN&" закачивается... "&Round($aData/$fSize*100)&"%", Round($aData/1024)&" Кб из "&Round($fSize/1024)&" Кб")
	Sleep(250)
Wend
ProgressOff ()
InetClose ($download)
EndFunc

Func _resume($StartByte)
	$i = 1
	If $pack > 0 Then
		ProgressOn("Закачка файла "&$filenameN, "Файл "&$filenameN&" закачивается..."&Round($ffSize/$fSize*100)&"%", Round($ffSize/1024)&" Кб из "&Round($fSize/1024)&" Кб")
		For $StartByte = $StartByte To $fSize Step $pack
			ConsoleWrite(@CRLF&"Запрос №"&$i&@CRLF&"Стартовый байт запроса = "&$StartByte&@CRLF&"Конечный байт запроса = "&$StartByte+$pack-1&@CRLF&"Всего байт в файле = "&$fSize&@CRLF)
			_request($StartByte,$StartByte+$pack-1)
			ProgressSet(Round(($StartByte+$pack-1)/$fSize*100), "Файл "&$filenameN&" закачивается... "&Round(($StartByte+$pack-1)/$fSize*100)&"%", Round(($StartByte+$pack-1)/1024)&" Кб из "&Round($fSize/1024)&" Кб")
			$i += 1
		Next
		ProgressOff()
	Else
		TrayTip ("Внимание","Файл закачиваеться в фоновом режиме, ни каких уведомлений о состояянии загрузки не будет выводиться до завершения загрузки.",5000,1)
		_request($StartByte,'')
	EndIf
EndFunc

Func _request($SB,$EB)
	$host = StringFindSE ($link,'http://','/',1,1)
	$oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
	$oHTTP.Open("GET", $link)
	$oHTTP.SetRequestHeader("Host",$host)
	$oHTTP.SetRequestHeader("Accept", "*/*")
	$oHTTP.SetRequestHeader("User-Agent", "Downloader/1.0 (compatible)")
	$oHTTP.SetRequestHeader("Range", "bytes="&$SB&"-"&$EB)
	$oHTTP.SetRequestHeader("Referer",'http://'&$host&'/')
	$oHTTP.setTimeouts(5000, 5000, 15000, 15000)
	$oHTTP.Send("")
	ConsoleWrite($oHTTP.Status & "/" & $oHTTP.StatusText&@CRLF)
	$oHTTP.WaitForResponse
	FileWrite ($file,$oHTTP.ResponseBody)
EndFunc

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $st_count,$source_start,$source_count,$source
    $st_count = StringSplit($start,"")
    $source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
    $source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
    $source = StringMid ($string,$source_start,$source_count)
    Return $source
EndFunc


Вот... Жду комментов :-[

:
Прикольно блин, пока тестировал - выкачал целую базу справочных пособий по Delphi :D первое что попалось нужного с прямыми ссылками.
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
В общем реализовал я какбэ многопоточность... Ну точнее многопроцессность :D но не для скачки одного файла по частям, а для скачки нескольких файлов одновременно... Работаю сейчас с консольной выдачей.. тяжко правда ориентироваться в буржуйской справке, а в старой русской неточные данные... Хотя поэксперементировав всёже разобрался :smile:
В общем получается по-тиху, организовал менеджер, который при добавлении закачки запускает процесс, с параметрами закачки (пути откуда и куда качать) и через консоль считывает информацию о текущем состоянии загрузки... Пока просто качает, а не докачивает, ещё не совмещал...
Вот такая вот лабудень... Как оказалось - нет ничего невозможного ;)
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Короче, без лишних предисловий выкладываю свою pre-beta v0.1 версию Downloader'а ;D
Что тут реализовано пока:
Качает по любым прямым и почти прямым ссылкам (атачи с форума потянет :smile: ).
По ссылкам с редиректами (до 3х редиректов подряд).
При обрыве можно докачать.
Удобный интерфейс (ну мне удобно :blum:)
В общем первая тестовая версия (просто услышать мнение со стороны)...

(1009.9Kb)

ЗЫ: в настройках все чекбоксы в режиме disabled не потому, что мне чего-то жалко, а потому, что это просто чекбоксы, без какого-либо смысла :D
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Пытался опять таки вернуться к многопоточной скачке - опять обломался, заменил WinHttpReques.5.1 на Microsoft.XMLHTTP (команды относительно одинаковые), в нем есть такая фича, когда создаешь запрос:
Код:
$objXMLHTTP.Open("GET","http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx",True)

Вот этот последний параметр True означает что должна использоваться "асинхронная" передача пакетов, а состояние закачки должно получатся командой $objXMLHTTP.readyState... т.е. После вызова $objXMLHTTP.Send скрипт должен продолжать исполнение, а не дожидаться ответа... но он, :Censored:, ждёт его всё равно !
Уже даже не знаю что делать, ибо это создаёт проблему не только в многопоточной скачке, но и в докачке, т.к. сейчас докачка оганизована по-пакетно т.е. делается несколько запросов на несколько кусков файлов, для того чтобы между запросами выводить состояние закачки (сколько байт получено)... Я вообще в замешательстве... Потому что фик с ним - докачка обычных файлов может и по-пакетно проходить, сильно от этого много не теряется, а вот загрузка с файлообменников таким образом не возможна, т.к. запрос уже второго и последующих пакетов будет игнорироваться... Может всё таки кто-нибудь мне подскажет как отправлять http пакеты TCPSend'ом ?
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Посидев ещё ночь над этим всем разобрался как отправлять/получать http пакеты обычным TCPSend'ом. Всё было не сложно... Даже разобрался как без проблем отделить заголовки ответа от его тела... Разобрался даже в каком формате приходит ответ (строковый/бинарный) и опять обломался... При передаче пакета в строковом виде AutoIt не может нормально передать символ NULL, а вместо него передаёт его бинарное представление (т.е. 0x00), да и вообще с ним работать не может, ну это болт с ним, сделал по-геморойному: Заменяю в полученом пакете 0x00 на <NULL>, сохраняю в файл, потом открываю этот файл в бинарном режиме, читаю, заменяю бинарное представление моего <NULL> (3C4E554C4C3E) на 00, вроде всё должно быть отлично.
Солнце за окном встаёт, вроде разобрался, но рано радоваться - файл всё равно выходит кривой... Сравниваю размер с исходным файлом - вижу, что где-то потерялся кусок файла... Сравниваю файлы в np++ и вижу, что в полученом мною файле нету символа тире... Подумал, что может что напортачил в скрипте, для перестраховки пролистал в консоли исходный свежеполученный пакет и вижу, что всёже я был прав и символ "тире" (-) просто не передается... Ну, переосилив свой гнев я подумал "а ну и пофик" и решил попробовать принять весь пакет в бинарном виде (TCPRecv($iSocket,102400000,1)), но и тут не всё так гладко, пакет вроде бы как бинарный, записан в одну строку, вида 0xДАННЫЕ, но всёж автоит его посчитал нифига не бинарным и даже насильно (через Binary ()) мне ему это доказать не удалось... В результате бинарные данные записываются в файл как строка, а BinaryToString () на полученные данные просто не реагирует...


ЗЫ: CreatoR, перенеси, пожалуйста, все таки эту тему в раздел новичков, а то полезняшка из неё пока никудышная... Не думал что столько гемора возникнет... Да и пишу как будто в блог :D
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
HukpoFuJl
Принимай данные в бинарном режиме. Записывай тоже. Скорее всего у тебя везде отбрасываются все байты после нулевого.
PS: TCPRecv(socket, maxlen, flag)
FileOpen($filepath, 16 + 2)
 
Автор
HukpoFuJl

HukpoFuJl

AццkuЙ HukpoFuJl
Сообщения
98
Репутация
38
Ха! "Если долго мучиться - что-нибудь получится!"... Вот так и у меня получилось =)) Получилось правдо как-то извратно, но я уже ничему не удивляюсь... Если получать в бинарном виде не весь файл целиком, а по частям, а потом в получившемcя итоге убрать все "0x" и дописать в начале "0x" - теоретически получится тоже самое, что и если получить весь код сразу... Вот и я так думал, но вот AutoIt видимо со мной согласен небыл... В итоге получается, что
Код:
While 1
	$sCurrentRecv = TCPRecv($iSocket,1048576,1)	;устанавливаем приём пакета в 1 Мб
												;(предполагается, что тестируемый файл
												;меньше этого размера.)
	If @error <> 0 Then ExitLoop
	If $sCurrentRecv <> "" Then
		$sRecv &= $sCurrentRecv
	EndIf
WEnd
$sRecv = Binary($sRecv)

И
Код:
While 1
	$sCurrentRecv = TCPRecv($iSocket,1024,1);устанавливаем приём пакета в 1 Кб
												;(в дальнейшем эти части объединяются
												;в одну строку.)
	If @error <> 0 Then ExitLoop
	If $sCurrentRecv <> "" Then
		$sRecv &= $sCurrentRecv
	EndIf
WEnd
$sRecv = StringReplace($sRecv,'0x','') ;убираем все 0х, которые вешаются перед к каждым пакетом.
$sRecv = Binary('0x'&$sRecv)
Возвращают разные значения!? По-пахивает бредятиной, но факт... Может мой ночной геморой сохранит в дальнейшем кому-нибудь нервы;)

Вот такого вида у меня получился скриптик, скачивающий gif картинку:
Код:
$sRecv = _TCP_HTTP ("autoit-script.3dn.ru","//_ld/0/26070065.gif")

$stringBin = BinaryToString($sRecv)

$headers = StringFindSE ($stringBin,'',@CRLF&@CRLF)
$header = StringSplit ($headers,@CRLF,1)
$fSize = StringFindSE ($headers,'Content-Length: ',@CRLF)
$pSize = StringLen($sRecv)-2
$dataT = StringReplace ($stringBin,$headers&@CRLF&@CRLF,'')
$fileH = FileOpen('Temp.gif',2)
FileWrite($fileH,$dataT)
FileClose ($fileH)

ConsoleWrite(@CRLF&'-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-')
ConsoleWrite(@CRLF&'+ Статус ответа '&$header[1])
For $i=2 To $header[0]
	ConsoleWrite(@CRLF&'! Заголовок №'&$i-1&':	'&$header[$i])
Next
ConsoleWrite(@CRLF&'> Размер файла = '&$fSize&' байт'& _
			@CRLF&'> Размер принятого пакета = '&$pSize&' байт')
ConsoleWrite(@CRLF&'-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-'&@CRLF&@CRLF)


Func _TCP_HTTP ($sHost,$sPage)
	TCPStartup()
		Local $sName_To_IP = TCPNameToIP($sHost)
		Local $iSocket = TCPConnect($sName_To_IP, 80)
		If $iSocket = -1 Then
			TCPCloseSocket($iSocket)
			Return SetError(1, 0, "")
		EndIf
		Local $sCommand = "GET "&$sPage&" HTTP/1.0" & @CRLF
		$sCommand &= "Host: " & $sHost & @CRLF
		$sCommand &="User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" & @CRLF
		$sCommand &= "Connection: close" & @CRLF & @CRLF

		Local $BytesSent = TCPSend($iSocket, $sCommand)
		If $BytesSent = 0 Then Return SetError(2, @error, 0)

		Local $sRecv = "", $sCurrentRecv
		While 1
			$sCurrentRecv = TCPRecv($iSocket,10240,1)
			If @error <> 0 Then ExitLoop
			If $sCurrentRecv <> "" Then
				$sRecv &= $sCurrentRecv
			EndIf
		WEnd
		$sRecv = StringReplace($sRecv,'0x','')
		$sRecv = Binary('0x'&$sRecv)

	TCPCloseSocket($iSocket)
	TCPShutdown()
	Return $sRecv
EndFunc

Func StringFindSE($string,$start,$end,$start_occ=1,$end_occ=1,$cas=0)
    Local $source_start,$source_count,$source
    If $start <> '' Then
		Local $st_count
		$st_count = StringSplit($start,'')
		$source_start = StringInStr($string,$start,$cas,$start_occ) + $st_count[0]
	Else
		$source_start = 1
	EndIf
	If $end <> '' Then
		$source_count = StringInStr($string,$end,$cas,$end_occ,$source_start) - $source_start
	Else
		$source_count = -1
	EndIf
	$source = StringMid ($string,$source_start,$source_count)
	Return $source
EndFunc


SyDr сказал(а):
А ты файл в бинарном режиме открывал? (Просто для проверки :smile:)
Ну разумеется :smile:

Kaster сказал(а):
HukpoFuJl
Принимай данные в бинарном режиме. Записывай тоже. Скорее всего у тебя везде отбрасываются все байты после нулевого.
PS: TCPRecv(socket, maxlen, flag)
FileOpen($filepath, 16 + 2)
Всё так и делаю, чуть ли не каждую строку отлаживаю в консоль... Ночь длинная, я успел проанализировать по пять раз каждую строку кода...
 

SyDr

Сидра
Сообщения
651
Репутация
158
Вот скажи мне, непонятливому, зачем ты сначала преобразуешь полученные бинарные данные (флаг - единичка) в строку, потом убираешь из этой строки 0x, затем добавляешь к этой строке 0x, после чего преобразуешь её обратно в бинарные данные? Кстати, насколько я знаю, в AutoIt строки являются Null-terminated String. В таком случае естественно, что ты теряешь всё, что идёт после 0x00
 
Верх