2012-02-04 10 views
8

Desidero caricare più file e formare variabili in uno script CGI, tutto in una richiesta HTTP. Credo che ciò richieda un post HTTP con codifica multipart/form-data. Ecco un esempio di modulo HTML che invia le informazioni richieste; Ho bisogno di inviare le stesse informazioni tramite l'applicazione:Registrare più file e valori di modulo utilizzando .NET (applicazione console)

<form action="/process.php" enctype="multipart/form-data" method="post"> 
<input type="text" name="foo" value="bar"> 
<input type="text" name="blah" value="baz"> 
<input type="file" name="file1"> 
<input type="file" name="file2"> 
<input type="file" name="file3"> 
</form> 

prega di notare che l'applicazione è un'applicazione C# .NET GUI (o console), non è un'applicazione ASP.Net.

+0

possibile duplicato di [Carica file con HTTPWebrequest (multipart/form-data)] (http://stackoverflow.com/questions/566462/upload-files-with-httpwebrequest-multipart-form-data) –

risposta

17

Ho fatto questo l'anno scorso sulla base di un codice che ho trovato. Supporta esattamente ciò che vuoi, sia file che valori.

Ecco la classe chiamata HttpForm:

public class HttpForm { 

    private Dictionary<string, string> _files = new Dictionary<string, string>(); 
    private Dictionary<string, string> _values = new Dictionary<string, string>(); 

    public HttpForm(string url) { 
     this.Url = url; 
     this.Method = "POST"; 
    } 

    public string Method { get; set; } 
    public string Url { get; set; } 

    //return self so that we can chain 
    public HttpForm AttachFile(string field, string fileName) { 
     _files[field] = fileName; 
     return this; 
    } 

    public HttpForm ResetForm(){ 
     _files.Clear(); 
     _values.Clear(); 
     return this; 
    } 

    //return self so that we can chain 
    public HttpForm SetValue(string field, string value) { 
     _values[field] = value; 
     return this; 
    } 

    public HttpWebResponse Submit() { 
     return this.UploadFiles(_files, _values); 
    } 


    private HttpWebResponse UploadFiles(Dictionary<string, string> files, Dictionary<string, string> otherValues) { 
     var req = (HttpWebRequest)WebRequest.Create(this.Url); 

     req.Timeout = 10000 * 1000; 
     req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; 
     req.AllowAutoRedirect = false; 

     var mimeParts = new List<MimePart>(); 
     try { 
      if (otherValues != null) { 
       foreach (var fieldName in otherValues.Keys) { 
        var part = new MimePart(); 

        part.Headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\""; 
        part.Data = new MemoryStream(Encoding.UTF8.GetBytes(otherValues[fieldName])); 

        mimeParts.Add(part); 
       } 
      } 

      if (files != null) { 
       foreach (var fieldName in files.Keys) { 
        var part = new MimePart(); 

        part.Headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\"; filename=\"" + files[fieldName] + "\""; 
        part.Headers["Content-Type"] = "application/octet-stream"; 
        part.Data = File.OpenRead(files[fieldName]); 

        mimeParts.Add(part); 
       } 
      } 

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

      req.ContentType = "multipart/form-data; boundary=" + boundary; 
      req.Method = this.Method; 

      long contentLength = 0; 

      byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n"); 

      foreach (MimePart part in mimeParts) { 
       contentLength += part.GenerateHeaderFooterData(boundary); 
      } 

      req.ContentLength = contentLength + _footer.Length; 

      byte[] buffer = new byte[8192]; 
      byte[] afterFile = Encoding.UTF8.GetBytes("\r\n"); 
      int read; 

      using (Stream s = req.GetRequestStream()) { 
       foreach (MimePart part in mimeParts) { 
        s.Write(part.Header, 0, part.Header.Length); 

        while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0) 
         s.Write(buffer, 0, read); 

        part.Data.Dispose(); 

        s.Write(afterFile, 0, afterFile.Length); 
       } 

       s.Write(_footer, 0, _footer.Length); 
      } 

      var res = (HttpWebResponse)req.GetResponse(); 

      return res; 
     } catch (Exception ex) { 
      Console.WriteLine(ex.Message); 
      foreach (MimePart part in mimeParts) 
       if (part.Data != null) 
        part.Data.Dispose(); 

      return (HttpWebResponse)req.GetResponse(); 
     } 
    } 

    private class MimePart { 
     private NameValueCollection _headers = new NameValueCollection(); 
     public NameValueCollection Headers { get { return _headers; } } 

     public byte[] Header { get; protected set; } 

     public long GenerateHeaderFooterData(string boundary) { 
      StringBuilder sb = new StringBuilder(); 

      sb.Append("--"); 
      sb.Append(boundary); 
      sb.AppendLine(); 
      foreach (string key in _headers.AllKeys) { 
       sb.Append(key); 
       sb.Append(": "); 
       sb.AppendLine(_headers[key]); 
      } 
      sb.AppendLine(); 

      Header = Encoding.UTF8.GetBytes(sb.ToString()); 

      return Header.Length + Data.Length + 2; 
     } 

     public Stream Data { get; set; } 
    } 
} 

è possibile utilizzarlo in questo modo:

var file1 = @"C:\file"; 
var file2 = @"C:\file2"; 

var yourUrl = "http://yourdomain.com/process.php"; 
var httpForm = new HttpForm(yourUrl); 
httpForm.AttachFile("file1", file1).AttachFile("file2", file2); 
httpForm.setValue("foo", "some foo").setValue("blah", "rarrr!"); 
httpForm.Submit(); 

farmi sapere se funziona per voi.

+0

Estremamente utile. – AgentFire

1

Ho dovuto fare la stessa cosa su un progetto l'anno scorso. Dopo un po 'guardando in giro, ho trovato questo:

Upload files with HTTPWebrequest (multipart/form-data)

La seconda risposta in là dovrebbe essere quello che stai cercando. Quando stavo cercando di farlo, mi sono imbattuto in qualche problema nel trovare quel metodo esatto per funzionare. Il problema è che C# .NET non ha alcun supporto per più coppie chiave/valore in un POST. Quindi è necessario costruire autonomamente l'intestazione del contenuto della richiesta HTTP. Credo che la risposta nel link sopra lo scriva direttamente sul flusso della richiesta. Sono stato in grado di convertire il codice trovato nel link sottostante per creare l'intestazione, quindi ho scritto i byte nel flusso della richiesta.

http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/

Il mio codice sembrava qualcosa di simile alla seguente (ho dovuto modificarlo sotto per la leggibilità perché un sacco di astratta come parte di tutto il progetto, in modo che non può essere sintatticamente perfetto).

public void BuildHeader(string contentType, string filename) 
    { 

     if ((contentType == null) || 
      (contentType.Length == 0)) 
     { 
      contentType = "application/octet-stream"; 
     } 

     // Create the boundary string for the POST message header 
     string boundary = "----------" + DateTime.Now.Ticks.ToString("x"); 

     // Build up the POST message header 
     StringBuilder sb = new StringBuilder(); 

     // The specific format used can be found in the HTTP protocol specs. 
     // The 'name' variable indicates the field-name, and the last variable 
     // added to the string before another boundary is the value for that field. 
     sb.Append("--"); 
     sb.Append(boundary); 
     sb.Append("\r\n"); 
     sb.Append("Content-Disposition: form-data; name=\""); 
     sb.Append("path"); 
     sb.Append("\""); 
     sb.Append("\r\n\r\n"); 
     sb.Append(fileName); 

     sb.Append("--"); 
     sb.Append(boundary); 
     sb.Append("\r\n"); 
     sb.Append("Content-Disposition: form-data; name=\""); 
     sb.Append("contents"); 
     sb.Append("\"; fileName=\""); 
     sb.Append("abc"); 
     sb.Append("\""); 
     sb.Append("\r\n"); 
     sb.Append("Content-Type: "); 
     sb.Append(contentType); 
     sb.Append("\r\n"); 
     sb.Append("\r\n"); 

     using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 
     { 
      // Add the file contents to the POST message 
      byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))]; 
      int bytesRead = 0; 
      while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) 
      { 
       sb.Append(ASCIIEncoding.ASCII.GetString(buffer)); 
      } 

      // Get the byte array of the POST message, and its length 
      string totalContents = sb.ToString(); 
      byte[] totalUpload = Encoding.UTF8.GetBytes(totalContents); 
      int length = totalUpload.Length; 
     } 
    } 

Si noti che questo prepara solo un'intestazione per il caricamento di un singolo file.

Problemi correlati