Laden...

Bild mit multipart/form-data per WebClient auf Mediawiki-Seite hochladen

Erstellt von Ballom vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.936 Views
Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren
Bild mit multipart/form-data per WebClient auf Mediawiki-Seite hochladen

Hi

Komme seit einiger Zeit nicht mehr weiter. Möchte meine Mediawiki Seite mit meinem C# Bot erweitern, da ich sehr viele Daten aus einer Datenbank habe und die Seiten automatisch generieren will.

Jetzt scheitere ich am Upload eines .png Bildes.


private void cmdUploadIcon_Click(object sender, EventArgs e)
{
           string fileUrl = @"c:\users\???\desktop\icons\STONE.png";

            string fileForm = "";
            fileForm += "POST / HTTP/1.1\r\n";
            fileForm += "Content-Length: 54246\r\n";
            fileForm += "Content-Type: multipart/form-data; boundary=-----------------------------735323031399963166993862150\r\n\r\n";
            fileForm += "-----------------------------735323031399963166993862150\r\n";
            fileForm += "Content-Disposition: form-data; name=\"myIcon\"; filename=\"STONE.png\"\r\n";
            fileForm += "Content-Type: image/png\r\n\r\n";
            fileForm += "{content}\r\n";
            fileForm += "-----------------------------735323031399963166993862150--";

            byte[] data = File.ReadAllBytes(fileUrl);
            fileForm = fileForm.Replace("{content}", System.Text.Encoding.UTF8.GetString(data));
 
            StreamWriter sw = new StreamWriter(@"c:\users\???\desktop\stream.txt");
            sw.Write(fileForm);
            sw.Close();

            string token = GetCsrfToken();
            string xmlResponse = UploadFile("stone.png", fileForm, token);
            txtResponse.Text = xmlResponse;
}

string UploadFile(string fileName, string filePath, string token)
{
            var request = new NameValueCollection();
            request["format"] = "xml";
            request["action"] = "upload";
            request["filename"] = fileName;
            request["file"] = filePath;
            request["token"] = token;
            return GetResponse(request);
}

CookieWebClient webClient = new CookieWebClient();
string GetResponse(NameValueCollection nameValueCollection)
{
            var response = webClient.UploadValues(txtUrl.Text, "POST", nameValueCollection);
            return Encoding.UTF8.GetString(response);
}

Die Ausgabedatei stream.txt behinhaltet folgendes. Sieht meiner Meinung nach gut aus.

POST / HTTP/1.1
Content-Length: 54246
Content-Type: multipart/form-data; boundary=-----------------------------735323031399963166993862150

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="myIcon"; filename="STONE.png"
Content-Type: image/png

ýPNG

   
IHDR   ý   ý   ýýZ=    IDATxýgýeYrýwýýýý~{ýýýýý0ýýX@ý
4¨XCýýy=ý'ýFýý</ýýýý$kuýCg²ý;ýuým0ýýý
(und so weiter)
-----------------------------735323031399963166993862150--

Ich erhalte aber als Antwort immer:


<?xml version="1.0"?>
<api>
    <error code="badupload_file" info="File upload parameter &quot;file&quot; is not a file upload; 
      be sure to use &quot;multipart/form-data&quot; for your POST and include a filename in the 
      &quot;Content-Disposition&quot; header." xml:space="preserve">
         See http://wiki.domain.com/api.php for API usage. Subscribe to the mediawiki-api- 
         announce mailing list at &amp;lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api- 
         announce&amp;gt; for notice of API deprecations and breaking changes.
   </error>
</api>

Hab schon einiges ausprobiert, aber könnte gut sein, dass ich etwas grundlegendes falsch mache.
Hat da jemand eine Idee? Bin ehrlich gesagt ziemlich überfordert damit.
Finde aber auch keine Infos, wie das genau abläuft und die Doku hilft mir auch nicht weiter.

Mediawiki Upload Doku: https://www.mediawiki.org/wiki/API:Upload

God save the screen.

16.842 Beiträge seit 2008
vor 5 Jahren

Das Header zusammen bauen ist quatsch, denn das macht alles der Client für Dich.
Daher natürlich auch die Fehlermeldung.

Darüber hinaus solltest Du mittlerweile die HttpClient Klasse nehmen, WebClient gilt als veraltet.
Google-Suche nach httpclient upload file c#

Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren

Der Kern meiner Frage ist eigentlich folgender...

Wie bekomme ich die Datei stone.png hier rein:
request["file"] = <-----

God save the screen.

16.842 Beiträge seit 2008
vor 5 Jahren

Keine große Lust meinem Link zu folgen, erstes Beispiel anzuschauen und zu übernehmen, wa?

In eine NameValueCollection kommt kein Dateinhalt; so funktioniert HTTP nicht.
Dateien werden als Contents übertragen, NameValueCollection wird zum Header.
Aber selbst wenn Du die Datei in die NameValueCollection packst, wird der Post ein ungültiges Format haben.
Gründe siehe vorheriger Beitrag.

Wenn Du den WebClient trotzdem verwenden willst, schau einfach das Beispiel der offiziellen Doku an.
Ein bisschen mehr Eigeninitative wäre schön - und würde Dich schneller zum Ziel bringen.
[Hinweis] Wie poste ich richtig?

Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren

Wo du recht hast... 😃

Ich hänge immer noch!

Das ist jetzt mein bester Versuch:


async public Task<string> UploadImage(byte[] imgByte, string token)
{
            var requestContent = new MultipartFormDataContent();

            var imageContent = new ByteArrayContent(imgByte);
            imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
            imageContent.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse("image.png");

            requestContent.Add(new StringContent("format"), "xml");
            requestContent.Add(new StringContent("action"), "upload");
            requestContent.Add(new StringContent("filename"), "image.png");
            requestContent.Add(new StringContent("token"), token);
            requestContent.Add(imageContent, "file", "image.png");

            var message = await client.PostAsync("http://wiki.domain.com/api.php", requestContent);
            var result = await message.Content.ReadAsStringAsync();
            return result;
}

Das Problem: Ich muss das Token zwingend mit "Post" übergeben und zwar gleichzeitig mit dem File. Ich hab das Problem, dass die Parameter nicht erkannt werden und die Meldung kommt "kein token übergeben" oder ich kann die Parameter übergeben, aber das File kommt nicht an. Falls ich die Parameter in der Url übergebe, dann heisst es "Token muss per "Post" übergeben werden.

Ich schaff es einfach nicht, alles da rein zu packen... Aber eigentlich müsste das doch jetzt gehen? Oder ist da was falsch im Code?

Hinweis von Abt vor 5 Jahren

Keine Notwendigkeit den gesamten Beitrag in Schriftgröße 862343 zu schreiben.

God save the screen.

P
441 Beiträge seit 2014
vor 5 Jahren

Wenn ich die Doku der API richtig deute sollten die Parameter über die URL mit übergeben werden und nicht im Body.

Hast du dir einmal die bereits fertigen Bibliotheken angeschaut, die es für MediaWiki gibt?
https://www.mediawiki.org/wiki/API:Client_code

Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren

Wenn ich die Parameter ohne Token in der Url übergebe (sonst alles gleich wie obere funktion):


http://wiki.domain.com/api.php?format=xml&action=upload&filename=image.png

erhalte ich:


<?xml version="1.0"?>
   <api><error code="notoken" info="The &quot;token&quot; parameter must be set." 
      xml:space="preserve">
         See http://wiki.domain.com/api.php for API usage. Subscribe to the mediawiki-api-announce 
         mailing list at &amp;lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&amp;gt; 
         for notice of API deprecations and breaking changes.
   </error>
   </api>

Wenn ich die Parameter übergebe, MIT Token:


http://wiki.domain.com/api.php?format=xml&action=upload&filename=image.png&token=" + token

Erhalte ich:


<?xml version="1.0"?>
   <api>
      <error code="mustpostparams" info="The following parameter was found in the query string, but must 
         be in the POST body: token." xml:space="preserve">
            See http://wiki.domain.com/api.php for 
            API usage. Subscribe to the mediawiki-api-announce mailing list at 
           &amp;lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&amp;gt; for notice of 
         API deprecations and breaking changes.
      </error>
   </api>

Habe schon das DotNetWikiBotFramework angesehen, aber scheint nicht mehr aktuell zu sein. Erhalte nur Fehler und die Community ist nicht mehr aktiv.
Hab noch MoreBitsDotNet angeschaut, aber war auch nicht so berauschende Erfahrung.
Ich habs ja FAST geschafft 😃

God save the screen.

16.842 Beiträge seit 2008
vor 5 Jahren

Das einzige, was man Deinem rum gestocher entgegen halten kann ist, dass die Doku echt schlecht ist 😉

Gibt eben Parameter, die müssen im Body mitgegeben werden, und welche im Query.
Für mich jetzt aber auch nicht ganz ersichtlich, welche wo sein müssen.

Musst halt die Fehlermeldungen anschauen (wenigstens die sind ja eindeutig) und darauf reagieren.

Fehlermeldung:
The following parameter was found in the query string, but must
be in the POST body: token.

Verwende doch einfach mal die in der Dokumentation immer wieder genannte Sandbox.
Die zeigt Dir ja genau an, was Du wie schicken musst.

Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren

Die Sandbox bringt mir nicht viel, da ich die selben Eingaben mache und die selben Ausgaben erhalte.

Habe das Problem mal eingekreist:

Wenn ich die Parameter als FormUrlEncodedContent übergebe, dann erkennt er diese, aber natürlich fehlt dann das File, weil ich es so nicht hinzufügen kann.


var content = new FormUrlEncodedContent(new[]
{
   new KeyValuePair<string, string>("format", "xml"),
   new KeyValuePair<string, string>("action", "upload"),
   new KeyValuePair<string, string>("filename", "image.png"),
   new KeyValuePair<string, string>("token", token),
});

var message = await client.PostAsync("http://wiki.domain.com/api.php", content);

Wenn ich die Parameter mit MultipartFormDataContent übergebe, dann erkennt er die Parameter nicht.


Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("format", "xml");
parameters.Add("action", "upload");
parameters.Add("filename", "image.png");
parameters.Add("token", token);

MultipartFormDataContent form = new MultipartFormDataContent();
HttpContent content = new StringContent("fileToUpload");
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(content, "fileToUpload");
form.Add(DictionaryItems, "medicineOrder");

var stream = new FileStream(@"c:\users\???\desktop\icons\STONE.png", FileMode.Open);
content = new StreamContent(stream);
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
   Name = "fileToUpload",
   FileName = "STONE.png.txt"
};
form.Add(content);

var message = await client.PostAsync("http://wiki.domain.com/api.php", form);

Ich schaffs nicht, dass die Parameter UND das File übergeben wird!
Wer hat eine Idee?

God save the screen.

Ballom Themenstarter:in
31 Beiträge seit 2015
vor 5 Jahren

Keiner eine Idee?

God save the screen.

16.842 Beiträge seit 2008
vor 5 Jahren

Ich bekomm das Gefühl nicht los, dass Du Dir diesen Code von StackOverflow oder CodeProject kopiert hast, ohne, dass Du ihn verstehen oder anwenden willst.
Vermutlich aus einer Antwort, die so gar nicht funktioniert. Und das Forum soll Dir jetzt den Code machen.

Allein

HttpContent content = new StringContent("fileToUpload");
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(content, "fileToUpload");
form.Add(DictionaryItems, "medicineOrder");

zeigt mir das, dass Du das kopiert hast - ohne, dass Du es Deinen Dingen angepasst hast.

fileToUpload oder medicineOrder haste Dir ja jetzt nicht aus dem Finger gezogen. Ist ja auch nicht Teil der API.
Ich wette den Code mit den Parametern findet man via Google.

Und ehrlich gesagt fühl ich mich verarscht, wenn Du irgendwo Code kopierst und wir sollen Dir das jetzt flicken.
So funktioniert ein Forum nicht. Das ist ziemlich mies.

16.842 Beiträge seit 2008
vor 5 Jahren

Ich wette den Code mit den Parametern findet man via Google.

Gewonnen.

How to post file and data to api using httpclient C#

Einfach Code kopiert - inkl. medicineOrder 👍