I think about this:
<?php$gameId = intval( $_GET['gameId'] );$version = intval( $_GET['version'] );if ( $gameId && $version ){ $files = array(); $rootDir = $_SERVER['DOCUMENT_ROOT'] . '/res_files'; $scanDir = sprintf( '%s/%d/%d', $rootDir, $gameId, $version ); $dirContent = scandir($scanDir); if ($dirContent) { foreach ( $dirContent as $entry ) { if ( !is_file( sprintf( '%s/%s', $scanDir, $entry ) ) ) { continue; } $files[$entry] = filesize( sprintf( '%s/%s', $scanDir, $entry ) ); } echo json_encode ( $files ); }}?> I checked, it perfectly works - returs files list and size for each file. But now i have other tasks to do now, so i'll return to this question later. Here my code for example, it can download one file, but getting several files is not complete.
#ifndef __GIPE_HTTP_DOWNLOAD_H__
#define __GIPE_HTTP_DOWNLOAD_H__
#define HTTP_OK 200
class HttpAnswer final
{
typedef std::map<std::string, std::string> StringMap;
public:
HttpAnswer() = default;
inline bool HasField(const std::string& name) const
{
return mFields.find(name) != mFields.end();
}
const std::string& GetValue(const std::string& name) const;
bool GetAsUnsigned(const std::string& name, size_t& res) const;
const std::string& GetVersion() const { return mVersion; }
int ReceiveAnswer(sf::TcpSocket& socket);
private:
int ParseFirstLine(const std::string& line);
bool GetLine(sf::TcpSocket& socket, std::string& line);
StringMap mFields;
std::string mVersion;
};
class HttpDownload final
{
public:
struct UpdateInfo
{
float mCurrentComplete = -1.0f; // if less than zero - is unknown
unsigned mSpeed = 0;
};
typedef std::function<void(const std::string& filename,
const UpdateInfo& info)> DownloadUpdateCallback;
HttpDownload() = default;
~HttpDownload();
bool Connect(const std::string& site);
bool IsOk() const { return mIsOk; }
template<typename T>
void SetUpdateCallback(T* owner,void (T::*func)(const std::string& filename,
const UpdateInfo& info))
{
mUpdateCallback = std::bind(func, owner, std::placeholders::_1,
std::placeholders::_2);
}
void SetUpdateCallback(void (*func)(const std::string& filename,
const UpdateInfo& info))
{
mUpdateCallback = std::bind(func, std::placeholders::_1,
std::placeholders::_2);
}
bool DownloadOne(const std::string& uri, std::FILE* file);
bool GetPack(const std::string& script, const std::string& game,
const std::string& version);
private:
void CalcStatistics();
bool MakeRequest(const std::string& uri);
bool Download(std::FILE* file);
void CompleteOne();
bool mIsOk = false;
sf::TcpSocket mSocket;
std::string mSite;
sf::Clock mClock;
size_t mCurrentSize = 0; // size of current downloading file if known
size_t mCurrentTotal = 0; // amount of downloaded bytes for current file
float mCurrentPercents = 0.0f;
size_t mAllSize = 0; // total size for all files
size_t mTotal = 0; // total downloaded
float mAllPercents = 0.0f;
unsigned int mSpeed = 0;
float mLastTime = 0;
DownloadUpdateCallback mUpdateCallback;
};
#endif
#include "pch.h"
#include "net/httpdownload.h"
#define UPDATE_INTERVAL 1.0f
static const std::string CONTENT_LENGTH = "Content-Length";
//////////////////////////////////////////////////////////////////////////
/// HttpAnswer
const std::string& HttpAnswer::GetValue(const std::string& name) const
{
assert(HasField(name));
return mFields.find(name)->second;
}
bool HttpAnswer::GetAsUnsigned(const std::string& name, size_t& res) const
{
const std::string& val = GetValue(name);
char* end;
auto value = strtoul(val.c_str(), &end, 10);
if (errno != ERANGE)
{
res = value;
return true;
}
return false;
}
bool HttpAnswer::GetLine(sf::TcpSocket& socket, std::string& line)
{
line.clear();
char ch;
size_t received = 0;
while (socket.receive(&ch, 1, received) == sf::Socket::Done)
{
if (received == 0)
{
return false;
}
if (ch != '\r' && ch != '\n')
{
line += ch;
}
if (ch == '\n')
{
break;
}
}
return true;
}
int HttpAnswer::ParseFirstLine(const std::string& line)
{
// HTTP/1.1 200 OK
std::string beg = line.substr(0, 5);
if (beg != "HTTP/")
{
return -1;
}
mVersion = line.substr(5, 3);
std::string code = line.substr(9);
char* end;
auto val = strtoul(code.c_str(), &end, 10);
if (errno != ERANGE)
{
return val;
}
return -1;
}
int HttpAnswer::ReceiveAnswer(sf::TcpSocket& socket)
{
std::string line;
int res = 0;
bool first = true;
do
{
if (!GetLine(socket, line))
{
return -1;
}
if (line.empty())
{
break;
}
if (first)
{
first = false;
res = ParseFirstLine(line);
if (res != HTTP_OK)
{
return res;
}
}
size_t pos = line.find_first_of(':');
if (pos != std::string::npos)
{
std::string field = line.substr(0, pos);
assert(!HasField(field));
pos += 2;
if (pos < line.length())
{
mFields[field] = line.substr(pos);
}
}
} while (!line.empty());
return res;
}
//////////////////////////////////////////////////////////////////////////
/// HttpDownload
HttpDownload::~HttpDownload()
{
mSocket.disconnect();
}
bool HttpDownload::Connect(const std::string& site)
{
assert(!mIsOk);
mSite = site;
sf::Socket::Status status = mSocket.connect(mSite.c_str(), 80);
mIsOk = status == sf::Socket::Done;
return mIsOk;
}
inline void HttpDownload::CalcStatistics()
{
auto elapsed = mClock.getElapsedTime();
auto seconds = elapsed.asSeconds();
mSpeed = seconds > 0 ? (unsigned) ((float) mCurrentTotal / seconds) : 0;
mCurrentPercents = 0;
if (mCurrentSize != 0)
{
mCurrentPercents = (float) mCurrentTotal / (float) mCurrentSize;
}
mAllPercents = 0;
if (mAllSize != 0)
{
mAllPercents = (float) mTotal / (float) mAllSize;
}
if (seconds - mLastTime > UPDATE_INTERVAL)
{
mLastTime = seconds;
UpdateInfo info;
if (mCurrentSize > 0)
{
info.mCurrentComplete = mCurrentPercents;
}
info.mSpeed = mSpeed;
if (mUpdateCallback)
{
mUpdateCallback("", info);
}
}
}
void HttpDownload::CompleteOne()
{
UpdateInfo info;
info.mCurrentComplete = 1.0f;
info.mSpeed = 0;
if (mUpdateCallback)
{
mUpdateCallback("", info);
}
}
bool HttpDownload::DownloadOne(const std::string& uri, std::FILE* file)
{
if (!MakeRequest(uri))
{
return false;
}
HttpAnswer answer;
if (answer.ReceiveAnswer(mSocket) != HTTP_OK)
{
return false;
}
if (answer.HasField(CONTENT_LENGTH))
{
if (!answer.GetAsUnsigned(CONTENT_LENGTH, mCurrentSize))
{
// warning
}
}
return Download(file);
}
bool HttpDownload::MakeRequest(const std::string& uri)
{
char* templ =
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3\r\n"
"Accept-Encoding: chunked\r\n"
"Vary: Accept-Encoding\r\n"
"Content-Type: application/octet-stream\r\n"
"Connection: keep-alive\r\n\r\n";
const size_t bufsize = 512;
char dest[bufsize];
#if defined(WINDOWS)
sprintf_s(dest, bufsize, templ, uri.c_str(), mSite.c_str());
#else
sprintf(dest, templ, uri.c_str(), mSite.c_str());
#endif
std::string req(dest);
return mSocket.send(req.c_str(), req.length()) == sf::Socket::Done;
}
bool HttpDownload::Download(std::FILE* file)
{
assert(file);
mCurrentTotal = 0;
mLastTime = 0.0f;
const size_t size = 4096;
char* buf[size];
size_t received = 0;
sf::Socket::Status status;
mClock.restart();
while ((status = mSocket.receive(buf, size, received)) == sf::Socket::Done &&
received > 0)
{
mCurrentTotal += received;
mTotal += received;
std::fwrite(buf, 1, received, file);
CalcStatistics();
}
CompleteOne();
return mCurrentSize == 0 ? true : mCurrentTotal == mCurrentSize;
}
bool HttpDownload::GetPack(const std::string& script, const std::string& game,
const std::string& version)
{
char* templ = "%s?gameId=%s&version=%s";
const size_t bufsize = 512;
char dest[bufsize];
#if defined(WINDOWS)
sprintf_s(dest, bufsize, templ,
script.c_str(), game.c_str(), version.c_str());
#else
sprintf(dest, templ,
script.c_str(), game.c_str(), version.c_str());
#endif
std::string req(dest);
if (!MakeRequest(req))
{
return false;
}
HttpAnswer answer;
if (answer.ReceiveAnswer(mSocket) != HTTP_OK)
{
return false;
}
size_t len = 0;
if (!answer.HasField(CONTENT_LENGTH) ||
!answer.GetAsUnsigned(CONTENT_LENGTH, len))
{
return false;
}
char* buf = (char*) malloc(len);
size_t received = 0;
mSocket.receive(buf, len, received);
if (received != len)
{
return false;
}
/// TODO: parse JSON array
std::string s(buf, len);
printf("%s\n", s.c_str());
free(buf);
return true;
}
How i plan to use it:
void UpdateStats(const std::string& fname, const HttpDownload::UpdateInfo& info)
{
printf("Loaded %.3d%% av. speed: %dKb/s \r",
(int) (info.mCurrentComplete * 100), info.mSpeed / 1024);
}
int _tmain(int argc, _TCHAR* argv[])
{
std::srand((unsigned)time(0));
HttpDownload http;
if (http.Connect("www.familyxoft.ru"))
{
std::FILE* file = std::fopen("test.pak", "wb");
if (!file)
{
printf("Can't open file for writing\n");
return 1;
}
http.SetUpdateCallback(&UpdateStats);
//http.GetPack("/path/getfiles.php", "1", "1"); // downloading several files
http.DownloadOne("/path/to/file/res.pak", file);
std::fclose(file);
}
return 0;
}