@Undressmyfuture

Как организовать обмен файлами между PHP сервером и C# десктоп приложением?

Всем привет. Разрабатываю своё первое клиент-серверное приложение, и есть задача скачивать файлы с сервера и закачивать обратно. Знаю, что самый простой способ - WebClient, но у меня задача более сложная - сами файлы должны храниться скрытно, и нужно выполнять определенную логику на стороне сервера (выдавать тот или иной файл, или вообще отказывать и т.д.).
Алгоритм вижу примерно такой:
  • В c# отправляю get-запрос к php-скрипту на сервере
  • PHP получает запрос, отказывает и возвращает error, либо находит нужный файл и возвращает его byte array
  • В C# получаю ответ, через streamreader считываю поток, сохраняю файл

Я совсем бред какой-то несу, или примерно так это и должно происходить?
Php и сяшарп немного умею, webrequest-ы тоже, но дальше как-то не. Даже не знаю, с какой стороны подступиться, если гуглить - вываливаются только примеры со скачиванием через браузер, не то. Наверняка ведь еще нюансы упускаю - шифрование? проверка контрольной суммы? и т.д.
Понимаю что вопрос пространный, но мне достаточно просто пинка в нужном направлении. Ответ в стиле "иди почитай про http сокеты" is fine too :)
  • Вопрос задан
  • 874 просмотра
Пригласить эксперта
Ответы на вопрос 2
firedragon
@firedragon
Не джун-мидл-сеньор, а трус-балбес-бывалый.
Получение файлов и их хранение это 2 совершенно не связанные задачи,

Отправка файла php
https://habr.com/ru/post/151795/

прием файла
WebRequest request = WebRequest.Create("http://somesite.com/private/myfile.txt");
            WebResponse response = request.GetResponse();
            using (Stream stream = response.GetResponseStream())            
            using (StreamReader reader = new StreamReader(stream))
                {
                    string line = "";
                    while ((line = reader.ReadLine()) != null)
                    {
                        Console.WriteLine(line);
                    }                     
            }
            response.Close();
Ответ написан
@Undressmyfuture Автор вопроса
Оставлю здесь то, что наковырял, может кому пригодится (пока только по скачиванию файла):
  • Отправлять запрос из C# приложения лучше через класс HttpWebRequest, он возвращает HttpWebResponse, у которого доступно больше полезных штук;
  • Чаще всего используется метод запроса POST;
  • Отдача файлов php скриптом универсальна для скачивания как браузером, так и c# приложением;
  • Отдачу файлов можно сделать разными способами, самый простой - функция php readfile($pathToFile);
  • одновременно с отдачей файлов php-скрипт может отдавать так называемые headers - можно сказать, "сопроводительную документацию" к ответу. Например, если выполнить строку header("HTTP/1.1 403 Forbidden"); то функция C# GetResponse() вернет соответствующий Exception. Еще в headers можно прописать имя файла:
    header('Content-Disposition: attachment; filename=' . basename($filepath));
  • Для адекватного скачивания браузером нужно прописать несколько headers, посмотреть в документации по php;
  • В C# приложении получить headers можно через свойство HttpWebResponse.Headers;
  • Файл приходит как поток байтов, и сохранить его как файл можно разными способами. Мне больше всего приглянулся способ через FileStream.


Ну и собственно немного индусского кода:
C#:
using System.Net;
using System.IO;
///returns: путь к файлу при успешном скачивании, иначе текст ошибки
public static string DownloadDistr()
{
    string folder = "C:\downloads";
	//формирую строку запроса
    string data = "email=" + userEmail + "&" + "password=" + userPassword;
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://yoursite.com/downloadfile.php");
    request.Method = "POST";
    byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(data);
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = byteArray.Length;

    //отправляю web запрос
    try
    {
        using (Stream dataStream = request.GetRequestStream())
        {
            dataStream.Write(byteArray, 0, byteArray.Length);
        }
    }
    catch (Exception ex)
    {
        return "Error: " + ex.Message;
    }

    //получаю ответ
    try
    {
        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
        {
            if (response.StatusCode != HttpStatusCode.OK)
            {
                return response.StatusCode.ToString();
            }
            //получаю из headers имя файла
            WebHeaderCollection headers = response.Headers;
            //вот тут как-то тупо, но и так сойдет
            string checkFilename = headers["Content-Disposition"].Split('=').Last(); 
            string pathToSave = Path.Combine(folder, checkFilename);

            //сохраняю файл через байт-буфер и FileStream
            byte[] buffer = new byte[1024];
            int read;
            using (FileStream download = new FileStream(pathToSave, FileMode.Create))
            {
                using (Stream stream = response.GetResponseStream())
                {
                    while ((read = stream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        download.Write(buffer, 0, read);
                    }
                }
            }
            return pathToSave;
        }
    }
    catch (Exception ex)
    {
        return "Error: " + ex.Message;
    }
}


PHP:
<?php
$userEmail = "unknownid";
$userPassword = "unknownPass";
//получаю данные из запроса
if (isset($_POST['email']) && isset($_POST['password'])) {
    $userEmail = strip_tags($_POST['email']);
    $userPassword = strip_tags($_POST['password']);
}  else {
    echo ('Error request');
    return;
}
//каким-то образом проверяю данные, могу запретить скачивание
if($userEmail != "correctEmail" || $userPassword != "correctpassword") {
    header("HTTP/1.1 403 Forbidden");
    return;
}
//путь к отдаваемому файлу
$filepath = "download/file.zip";
if (!file_exists($filepath)) {
    header("HTTP/1.1 404 Not Found");
    return;
}
try {
	//на всякий случай очищаю буфер вывода
    if (ob_get_level()) {
        ob_end_clean();
    }
	//header из которого получу имя файла
    header('Content-Disposition: attachment; filename=' . basename($filepath));
	
	//а эти headers нужны в первую очередь для браузера, оставил на всякий случай
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($filepath));+
	//отдаю файл
    readfile($filepath);
    exit;
} catch (Exception $ex) {
    echo $ex->getMessage();
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы