Envie um arquivo via HTTP POST com C #

95

Tenho pesquisado e lido sobre isso e não consegui encontrar nada realmente útil.

Estou escrevendo um pequeno aplicativo C # win que permite ao usuário enviar arquivos para um servidor web, não por FTP, mas por HTTP usando POST. Pense nisso como um formulário da web, mas em execução em um aplicativo do Windows.

Eu tenho meu objeto HttpWebRequest criado usando algo assim

HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest 

e também definir as Method, ContentTypee ContentLengthpropriedades. Mas isso é o mais longe que posso ir.

Este é o meu código:

HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest;
req.KeepAlive = false;
req.Method = "POST";
req.Credentials = new NetworkCredential(user.UserName, user.UserPassword);
req.PreAuthenticate = true;
req.ContentType = file.ContentType;
req.ContentLength = file.Length;
HttpWebResponse response = null;

try
{
    response = req.GetResponse() as HttpWebResponse;
}
catch (Exception e) 
{
}

Portanto, a minha pergunta é basicamente como posso enviar um arquivo (arquivo de texto, imagem, áudio, etc) com C # via HTTP POST.

Obrigado!

gabitoju
fonte

Respostas:

112

Usando o .NET 4.5 (ou .NET 4.0 adicionando o pacote Microsoft.Net.Http do NuGet), há uma maneira mais fácil de simular solicitações de formulário. Aqui está um exemplo:

private async Task<System.IO.Stream> Upload(string actionUrl, string paramString, Stream paramFileStream, byte [] paramFileBytes)
{
    HttpContent stringContent = new StringContent(paramString);
    HttpContent fileStreamContent = new StreamContent(paramFileStream);
    HttpContent bytesContent = new ByteArrayContent(paramFileBytes);
    using (var client = new HttpClient())
    using (var formData = new MultipartFormDataContent())
    {
        formData.Add(stringContent, "param1", "param1");
        formData.Add(fileStreamContent, "file1", "file1");
        formData.Add(bytesContent, "file2", "file2");
        var response = await client.PostAsync(actionUrl, formData);
        if (!response.IsSuccessStatusCode)
        {
            return null;
        }
        return await response.Content.ReadAsStreamAsync();
    }
}
Joshcodes
fonte
8
Se possível, poderia mostrar um exemplo simples de chamar esse método?
Jacobr365
10
o que é parâmetro paramString?
eran otzap
2
Obrigado, exemplo muito abrangente! @eranotzap o paramString é o valor real do parâmetro a ser enviado. O terceiro argumento para form.Addé opcional e útil apenas para arquivos.
StockBreak,
1
@Liam, concordo plenamente. O código assíncrono foi removido da minha resposta de 2013 para manter as coisas simples. Alterá-lo de volta para um método assíncrono está em minha lista de tarefas, pois a maioria dos desenvolvedores C # deve estar confortável com isso neste ponto.
Joshcodes
1
@Ammar, que eu saiba, acho que você teria que ler o arquivo em um stream ou byte [] e usar StreamContent ou ByteArrayContent respectivamente.
Joshcodes de
51

Para enviar apenas o arquivo bruto :

using(WebClient client = new WebClient()) {
    client.UploadFile(address, filePath);
}

Se você quiser emular um formulário de navegador com um <input type="file"/>, isso é mais difícil. Veja esta resposta para uma resposta multipart / form-data.

Marc Gravell
fonte
(é claro que você pode adicionar cabeçalhos / credenciais / etc normalmente)
Marc Gravell
1
Obrigado, usei com algo simples e não funcionou. Agora, como você disse, preciso simular um arquivo de entrada do navegador, algo como este <intput type = "file" name "userFile" />.
gabitoju
1
Usei o código acima e recebi um erro como: Exceção de argumento não tratada pelo código do usuário: {"Formatos de URI não são suportados."}. Como posso fazer isso? protegido void Page_Load (remetente do objeto, EventArgs e) {string address = "http: www.testproject.com/SavedFiles"; string filepath = @ "D: \ test \ FileOperations \ testfile.txt"; usando (cliente WebClient = novo WebClient ()) {client.UploadFile (endereço, caminho de arquivo); }}
Sudha
1
@Sudha, você tentou usar um endereço da web real? http://www.testproject.com/SavedFiles- observe o//
Marc Gravell
7

Para mim, client.UploadFileainda envolveu o conteúdo em uma solicitação multiparte, então tive que fazer assim:

using (WebClient client = new WebClient())
{
    client.Headers.Add("Content-Type", "application/octet-stream");
    using (Stream fileStream = File.OpenRead(filePath))
    using (Stream requestStream = client.OpenWrite(new Uri(fileUploadUrl), "POST"))
    {
        fileStream.CopyTo(requestStream);
    }
}
Meelis Pruks
fonte
5

Eu tive o mesmo problema e este código a seguir respondeu perfeitamente a este problema:

//Identificate separator
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
//Encoding
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

//Creation and specification of the request
HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url); //sVal is id for the webService
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
wr.KeepAlive = true;
wr.Credentials = System.Net.CredentialCache.DefaultCredentials;

string sAuthorization = "login:password";//AUTHENTIFICATION BEGIN
byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(sAuthorization);
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
wr.Headers.Add("Authorization: Basic " + returnValue); //AUTHENTIFICATION END
Stream rs = wr.GetRequestStream();


string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; //For the POST's format

//Writting of the file
rs.Write(boundarybytes, 0, boundarybytes.Length);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(Server.MapPath("questions.pdf"));
rs.Write(formitembytes, 0, formitembytes.Length);

rs.Write(boundarybytes, 0, boundarybytes.Length);

string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
string header = string.Format(headerTemplate, "file", "questions.pdf", contentType);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
rs.Write(headerbytes, 0, headerbytes.Length);

FileStream fileStream = new FileStream(Server.MapPath("questions.pdf"), FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
    rs.Write(buffer, 0, bytesRead);
}
fileStream.Close();

byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
rs.Write(trailer, 0, trailer.Length);
rs.Close();
rs = null;

WebResponse wresp = null;
try
{
    //Get the response
    wresp = wr.GetResponse();
    Stream stream2 = wresp.GetResponseStream();
    StreamReader reader2 = new StreamReader(stream2);
    string responseData = reader2.ReadToEnd();
}
catch (Exception ex)
{
    string s = ex.Message;
}
finally
{
    if (wresp != null)
    {
        wresp.Close();
        wresp = null;
    }
    wr = null;
}
Thomas BLANCHET
fonte
como você recebe os dados e salva o arquivo no disco na outra extremidade?
KumarHarsh
3

Você precisa gravar seu arquivo no fluxo de solicitação:

using (var reqStream = req.GetRequestStream()) 
{    
    reqStream.Write( ... ) // write the bytes of the file
}
Pop Catalin
fonte
1

Para postar arquivos a partir de matrizes de bytes:

private static string UploadFilesToRemoteUrl(string url, IList<byte[]> files, NameValueCollection nvc) {

        string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

        var request = (HttpWebRequest) WebRequest.Create(url);
        request.ContentType = "multipart/form-data; boundary=" + boundary;
        request.Method = "POST";
        request.KeepAlive = true;
        var postQueue = new ByteArrayCustomQueue();

        var formdataTemplate = "\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";

        foreach (string key in nvc.Keys) {
            var formitem = string.Format(formdataTemplate, key, nvc[key]);
            var formitembytes = Encoding.UTF8.GetBytes(formitem);
            postQueue.Write(formitembytes);
        }

        var headerTemplate = "\r\n--" + boundary + "\r\n" +
            "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" + 
            "Content-Type: application/zip\r\n\r\n";

        var i = 0;
        foreach (var file in files) {
            var header = string.Format(headerTemplate, "file" + i, "file" + i + ".zip");
            var headerbytes = Encoding.UTF8.GetBytes(header);
            postQueue.Write(headerbytes);
            postQueue.Write(file);
            i++;
        }

        postQueue.Write(Encoding.UTF8.GetBytes("\r\n--" + boundary + "--"));

        request.ContentLength = postQueue.Length;

        using (var requestStream = request.GetRequestStream()) {
            postQueue.CopyToStream(requestStream);
            requestStream.Close();
        }

        var webResponse2 = request.GetResponse();

        using (var stream2 = webResponse2.GetResponseStream())
        using (var reader2 = new StreamReader(stream2)) {

            var res =  reader2.ReadToEnd();
            webResponse2.Close();
            return res;
        }
    }

public class ByteArrayCustomQueue {

    private LinkedList<byte[]> arrays = new LinkedList<byte[]>();

    /// <summary>
    /// Writes the specified data.
    /// </summary>
    /// <param name="data">The data.</param>
    public void Write(byte[] data) {
        arrays.AddLast(data);
    }

    /// <summary>
    /// Gets the length.
    /// </summary>
    /// <value>
    /// The length.
    /// </value>
    public int Length { get { return arrays.Sum(x => x.Length); } }

    /// <summary>
    /// Copies to stream.
    /// </summary>
    /// <param name="requestStream">The request stream.</param>
    /// <exception cref="System.NotImplementedException"></exception>
    public void CopyToStream(Stream requestStream) {
        foreach (var array in arrays) {
            requestStream.Write(array, 0, array.Length);
        }
    }
}
fazedor
fonte
1
     public string SendFile(string filePath)
            {
                WebResponse response = null;
                try
                {
                    string sWebAddress = "Https://www.address.com";

                    string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                    byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
                    HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(sWebAddress);
                    wr.ContentType = "multipart/form-data; boundary=" + boundary;
                    wr.Method = "POST";
                    wr.KeepAlive = true;
                    wr.Credentials = System.Net.CredentialCache.DefaultCredentials;
                    Stream stream = wr.GetRequestStream();
                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";

                    stream.Write(boundarybytes, 0, boundarybytes.Length);
                    byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(filePath);
                    stream.Write(formitembytes, 0, formitembytes.Length);
                    stream.Write(boundarybytes, 0, boundarybytes.Length);
                    string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
                    string header = string.Format(headerTemplate, "file", Path.GetFileName(filePath), Path.GetExtension(filePath));
                    byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                    stream.Write(headerbytes, 0, headerbytes.Length);

                    FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                    byte[] buffer = new byte[4096];
                    int bytesRead = 0;
                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                        stream.Write(buffer, 0, bytesRead);
                    fileStream.Close();

                    byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                    stream.Write(trailer, 0, trailer.Length);
                    stream.Close();

                    response = wr.GetResponse();
                    Stream responseStream = response.GetResponseStream();
                    StreamReader streamReader = new StreamReader(responseStream);
                    string responseData = streamReader.ReadToEnd();
                    return responseData;
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
                finally
                {
                    if (response != null)
                        response.Close();
                }
            }
Masoud Siahkali
fonte
0

Usando o .NET 4.5 tentando realizar o upload do arquivo POST. Tentei a maioria dos métodos acima, mas sem sucesso. Encontre a solução aqui https://www.c-sharpcorner.com/article/upload-any-file-using-http-post-multipart-form-data

Mas eu não estou interessado, pois não entendo por que ainda precisamos lidar com a programação de baixo nível nesses usos comuns (deve ser tratado muito bem pelo framework)

bLaXjack
fonte