Envoyer un fichier via HTTP POST avec C #

95

J'ai cherché et lu à ce sujet et je ne pouvais rien de vraiment utile.

J'écris une petite application C # win qui permet à l'utilisateur d'envoyer des fichiers à un serveur Web, non pas par FTP, mais par HTTP en utilisant POST. Pensez-y comme un formulaire Web mais fonctionnant sur une application Windows.

J'ai mon objet HttpWebRequest créé en utilisant quelque chose comme ça

HttpWebRequest req = WebRequest.Create(uri) as HttpWebRequest 

et définissez également les propriétés Method, ContentTypeet ContentLength. Mais c'est jusqu'où je peux aller.

Voici mon morceau de code:

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) 
{
}

Ma question est donc de savoir comment envoyer un fichier (fichier texte, image, audio, etc.) avec C # via HTTP POST.

Merci!

gabitoju
la source
1
Veuillez vérifier stackoverflow.com/questions/15738847/…
Sudha

Réponses:

112

En utilisant .NET 4.5 (ou .NET 4.0 en ajoutant le package Microsoft.Net.Http de NuGet), il existe un moyen plus simple de simuler les demandes de formulaire. Voici un exemple:

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
la source
8
Si possible, pourrait montrer un exemple simple d'appeler cette méthode?
Jacobr365
10
quel est le paramètre paramString?
eran otzap
2
Merci, exemple très complet! @eranotzap la chaîne paramString est la valeur réelle du paramètre à envoyer. Le troisième argument de form.Addest facultatif et utile uniquement pour les fichiers.
StockBreak
1
@Liam, je suis entièrement d'accord. Le code asynchrone a été supprimé de ma réponse de 2013 pour simplifier les choses. Le ramener à une méthode asynchrone faisait partie de ma liste de tâches, car la plupart des développeurs C # devraient être à l'aise avec cela à ce stade.
Joshcodes
1
@Ammar, pas que je sache, je pense que vous devriez lire le fichier dans un flux ou un octet [] et utiliser respectivement StreamContent ou ByteArrayContent.
Joshcodes
51

Pour envoyer uniquement le fichier brut :

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

Si vous souhaitez émuler un formulaire de navigateur avec un <input type="file"/>, alors c'est plus difficile. Voir cette réponse pour une réponse multipart / form-data.

Marc Gravell
la source
(vous pouvez bien sûr ajouter des en-têtes / informations d'identification / etc comme d'habitude)
Marc Gravell
1
Merci, je l'ai utilisé avec quelque chose de simple et je n'ai pas travaillé. Maintenant, comme vous le dites, je dois émuler un fichier d'entrée de navigateur, quelque chose comme ceci <intput type = "file" name "userFile" />.
gabitoju
1
J'ai utilisé le code ci-dessus et j'ai obtenu une erreur du type: L'exception d'argument n'a pas été gérée par le code utilisateur: {"Les formats URI ne sont pas pris en charge."}. Comment puis-je faire ceci? protected void Page Load (expéditeur de l'objet, EventArgs e) {string address = "http: www.testproject.com/SavedFiles"; string filepath = @ "D: \ test \ FileOperations \ testfile.txt"; using (client WebClient = nouveau WebClient ()) {client.UploadFile (adresse, chemin de fichier); }}
Sudha
1
@Sudha avez-vous essayé d'utiliser une adresse Web réelle? http://www.testproject.com/SavedFiles- noter le//
Marc Gravell
7

Pour moi, j'ai client.UploadFiletoujours enveloppé le contenu dans une requête en plusieurs parties, je devais donc le faire comme ceci:

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
la source
5

J'avais eu le même problème et ce code suivant répondait parfaitement à ce problème:

//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
la source
comment recevez-vous les données et enregistrez-vous le fichier sur le disque à l'autre extrémité?
KumarHarsh
3

Vous devez écrire votre fichier dans le flux de requête:

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

Pour publier des fichiers à partir de tableaux d'octets:

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);
        }
    }
}
Doker
la source
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
la source
0

Utilisation de .NET 4.5 pour essayer d'effectuer le téléchargement de fichiers POST. J'ai essayé la plupart des méthodes ci-dessus mais en vain. Trouvé la solution ici https://www.c-sharpcorner.com/article/upload-any-file-using-http-post-multipart-form-data

Mais je ne suis pas enthousiaste car je ne comprends pas pourquoi nous devons encore gérer une programmation de bas niveau dans ces usages courants (devrait être bien géré par le framework)

bLaXjack
la source