Upload Image als Binary

4. August 2022 15:40

Hallo zusammen.

Im Dynamics Universum läuft wieder was anders habe ich das gefühl.
Und zwar möchte ich eine Datei, genauer ein Bild, aus einer Datei (Jpg, jpeg, png, etc.) einlesen und als Binären String/Text speichern. Dieser soll dann einer API übergeben werden die das Bild dann in einen Webshop läde.
Ich habe aber das gefühl, dass beim einlesen der Datei einzelne Character entweder falsche interpretiert werden, oder ganz ausgelassen werden.
Was mache ich:
Code:
TestFile.Open(FilePath, TextEncoding::Windows);   //Ohne das Encoding::Windows kommt totaler Quark bei raus.
TestFile.CreateInStream(TempInStream);

while not TempInStream.EOS do begin         //Ohne die while ließt er nur 4 von knapp 300.000 Zeichen meines Testbildes.
  TempInStream.Read(TestText);
  if TestText = '' then                                //Beim einlesen der Textstückchen filtert Stream.read leerzeichen anscheint raus. Dann passt die Datei aber nicht mehr.
    TestText := ' ';
  ImageAsBinary += TestText;
end;
Message(ImageAsBinary);

Vergleiche ich nun was hierbei raus kommt mit der orgiginal Datei (geöffnet z.B. Notepad++) stimmen schon sehr viele Zeichen überein. Manche aber halt nicht.
Ich habe es auch schon probiert Read(TestChar)... also Character für Character. Das ergebnis war noch etwas besser aber dennoch nicht perfekt. Und außerdem dauerte es ewig.

Am schönesten wäre natürlich:
Code:
TestFile.Open(FilePath);
TestFile.CreateInStream(TempInStream);
TempInStream.Read(ImageAsBinary);
TestFile.Close();
Message(ImageAsBinary);

Soll aber leider nicht sein.

Was mache ich falsch?
Was habe ich nicht bedacht?
Geht es vielleicht viel einfacher?

Danke euch für eure Antworten.

Re: Upload Image als Binary

4. August 2022 16:02

Hallo,

wenn du mit Binary arbeiten willst, lass das Encoding weg, und arbeite mit TempBLOB

Gruß, Fiddi

Re: Upload Image als Binary

5. August 2022 10:35

Danke fiddi für deine Antwort.
Ist ja schon etwas kurz und lässt alles offen.
Ich sehe den Sinn nicht über ein TempBlob zu gehen. Vielleicht erläuterst du mir den Mehrwert.
Wieso frage ich?
Mein erster Ansatz damals war folgender:
Code:
FileManagement.BLOBImportFromServerFile(TempBlob, FilePath); //Irgendwie muss die Datei ja erstmal vom Server ins BC bzw in den Speicher.
        TempBlob.CreateInStream(TempInStream);  //Jetzt will ich den Inhalt des Blobs von gerade lesen und öffne den Stream
        while not TempInStream.EOS do begin  //Jetzt lese ich den Stream in eine Text-Variable. Leider muss auch hier die while sein, da er in meinem Beispiel das erste mal nach 4 Zeichen sonst aufhören würde.
            TempInStream.Read(TestText);
            if TestText = '' then
                TestText := ' ';
            ImageAsBinary += TestText;
        end;


Bei diesem Ansatz kommt nur leider absoluter murks bei raus. Ich war der Sache auf den Grund gegangen. Dazu schaute ich mir "BLOBImportFromServerFile" genauer an:
Code:
procedure BLOBImportFromServerFile(var TempBlob: Codeunit "Temp Blob"; FilePath: Text)
    var
        OutStream: OutStream;
        InStream: InStream;
        InputFile: File;
    begin
        IsAllowedPath(FilePath, false);

        if not FILE.Exists(FilePath) then
            Error(FileDoesNotExistErr, FilePath);

        InputFile.Open(FilePath);
        InputFile.CreateInStream(InStream);
        TempBlob.CreateOutStream(OutStream);
        CopyStream(OutStream, InStream);
        InputFile.Close;
    end;


Oho siehe da. Der selbe Code wie bei mir oben (erster Eintrag) nur mit dem Umweg über ein TempBlob. Ich habe das Gefühl das der Defaultwert für das TextEncoding bei "File.Open" "MSDOS" ist. Da hier bei der Standardfunktion kein TextEncoding gesetzt wird wird versucht eine ANSI Datei per MSDOS Encryption zu lesen. Darum auch der Murks bei der Ausgabe. Alles nur vermutungen.

Daher habe ich den Code wie in meinem ersten Beitrag erstellt und ein wenig Rumprobiert. Bei UTF-8 und 16 kommen direkt Fehlermeldungen und es geht gar nicht. Bei MSDOS sieht es halt aus wie als wenn man gar nichts einträgt. Bei "Windows" sieht es dann annährend so aus wie es aussehen soll.

Ich vermute das Problem liegt an der Stelle wo die Datei in den Stream übergeben wird. Unabhänig vom TextEncoding habe ich den Verdacht das einige Zeichen missinterpretiert, igoniert oder sonst wie falsch behandelt werden.

Hier mal der Vergleich eines kleinen Abschnitts...
Origninaldatei (geöffnet in Notpadd++
Code:
ÿØÿà JFIF      ÿÛ C ÿÛ CÿÀ .è" ÿÄ              
ÿÄ µ   } !1AQa"q2‘¡#B±ÁRÑð$3br‚

Die "Code"-Ansicht hier schneidet auch viele Zeichen weg. In Notpadd++ steht noch sowas wie "NUL", "DEL", "SOH", "STX", "CAN", "EM", usw. genau das sind die Zeichen die BC falsch behandelt.

BC ohne TextEncoding
Code:
Ï ÓJFIF   ›C ›C ‹.Þ"   
Á }!1AQa"q2üæí#BŒRЭ$3bré


Hier gut zu sehen der Anfang, die ersten 4 Zeichen, passen schon überhaupt nicht.

Zu guter letzt mit TextEncoding::Windows und so wie ich es im ersten Beitrag gemacht habe
Code:
ÿØÿàJFIF  ÿÛCÿÛCÿÀ.è"ÿÄ  
ÿĵ }!1AQa"q2‘¡#B±ÁRÑð$3br‚

Hier gut zu sehen es fehlen irgendwie ein paar Zeichen von deben die nicht angezeigt werden und hier z.B. im Originalcode als Leerzeichen dargestellt werden.

Wie ich schon schrieb. Vielleicht denke ich auch komplett in die falsche Richtung. Ich kann ja nunmal nicht mehr als eigen Ideen rumprobieren oder rumlesen und das was ich finde ausprobieren. Bisher nunmal ohne Erfog. Daher bin ich nach 2 Tagen rumgeteste und probieren hier mit meinem Problem Vorstellig geworden.

Re: Upload Image als Binary

5. August 2022 10:59

Hallo,
Ist ja schon etwas kurz und lässt alles offen.
Ich sehe den Sinn nicht über ein TempBlob zu gehen. Vielleicht erläuterst du mir den Mehrwert


ganz einfach: Ein Textfeld ist immer kodiert, und wird in alle Richtungen umgewandelt. Nun meine Erklärung war deshalb etwas kurz, weil es Tonnen an Beispielen im Internet und auch in der Base-App gibt, wie man mit Binaries umgeht.
Die einzige Möglichkeit in BC mit Binaries umzugehen sind BLOBS. Deshalb heißen die so (Binary Large OBjects). :wink:
Da deine JFIF- Datei ja ein Binary Format ist, solltest du es auch als solches behandeln, und auch als BLOB Speichern.

Auf diese Binarydaten kannst du nicht ohne Tricks mit einer Textvariable zugreifen, und schon mal gar nicht das glauben, was der Notepad++ dir anzeigt. Wechselst du dort die Kodierung, zeigt er die etwas ganz anderes an.

Gruß Fiddi

Re: Upload Image als Binary

5. August 2022 11:43

fiddi hat geschrieben:Deshalb heißen die so (Binary Large OBjects)

Schon klar ;)

fiddi hat geschrieben:Nun meine Erklärung war deshalb etwas kurz, weil es Tonnen an Beispielen im Internet und auch in der Base-App gibt, wie man mit Binaries umgeht.

Auch klar. Habe ich auch vieles gefunden und auch als Youtube von z.B. "Erik Hougaard". Diese lesen eine Datei in ein Blobfeld ein uns zeigen es auf einer Page an. Jut. Habe ich probiert und geht. Daher habe ich mich auch gewundert warum es nicht geht wenn ich jetzt dieses BlobFeld nehme und mit einem Instream an eine Text Variable übergebe und diese dann an den WebShop übertrage. Je nach dem wie ich hin und her probiere kommt dann:
1. entweder schon bei POST send eine Rückmeldung das der Server intern einen fehler verursacht was bedeutet der Inhalt der Anfrage ist falsch
2. oder der Server nimmt die Anfrage entgegen, es wird ein Bildelement angelegt und das ist dann aber fehlerhaft. Kann nicht angezeigt werden. Wenn ich mir die Datei runter lade kann ich diese mit nichts öffnen.

Daher war ich schonmal so schlau und habe den weg weiter verfolgt und einfach mal das was ich in das TempBlob schreibe anschließend lese und einfach wieder auf den Server in eine Datei schreibe. Jetzt müsste ja nach Adam Riese und Graf Zahl das Bild wieder angezeigt werden oder? Selbstverständlich alles ohne Encodings. Nö ... Bild kann nicht angezeigt werden. Und ich habe sogar die mehrere der "Tonnen von Beispielen" ausprobiert, welches sich zu meist ähneln bis auf den Variablennamen ;)

fiddi hat geschrieben:Auf diese Binarydaten kannst du nicht ohne Tricks mit einer Textvariable zugreifen,

Also scheint das ja meinen Vermutungen zu entsprechen. Weil genau das brauche ich ja. Die Binärdaten als String/Text um diese mit einem HTTP Content zu verschicken.

Re: Upload Image als Binary

5. August 2022 12:25

Hallo,

ich wollte noch einen vorherigen Post noch einen Satz schreiben:
Wenn du die Daten allerdings im Internet übertragen möchtest, dann macht man das natürlich nicht im Binary- Format. Dafür benutzt man natürlich einen Text. :mrgreen:

Das Stichwort dazu heißt "Base64- Encoding"

Gruß Fiddi

Re: Upload Image als Binary

5. August 2022 12:48

fiddi hat geschrieben:Hallo,

ich wollte noch einen vorherigen Post noch einen Satz schreiben:
Wenn du die Daten allerdings im Internet übertragen möchtest, dann macht man das natürlich nicht im Binary- Format. Dafür benutzt man natürlich einen Text. :mrgreen:

Das Stichwort dazu heißt "Base64- Encoding"

Gruß Fiddi


Nicht immer ;)
https://shopware.stoplight.io/docs/admin-api/ZG9jOjEyNjI1Mzkw-media-handling#2-attach-resource-data-to-the-media-object
// POST http://localhost/api/_action/media/{mediaId}/upload?extension=jpg
// Content-Type: image/jpg

// binary file body


Und Beiträge aus dem Offiziellen Shopwarforum lauten:
Bist du dir sicher, dass Base64 schon durch die API supported wird? Mein letzter Stand ist als FILE/POST oder via URL (Bild liegt auf remote Host).


Das Bild einfach ohne BASE64 senden. BASE64 bläht hier nur den Payload auf und bringt nichts weil du über http problemlos Binary senden kannst.


Das nur zu deinem letzte Beitrag :wink:

ABER!

Durch unsere Diskussion habe ich nun, wie es scheint einen Lösungsweg gefunden. Und ganz ehrlich. Das habe ich nach Tagen suche nirgendwo im Netz gesehen.
Auslöser war dein einer Satz.
Auf diese Binarydaten kannst du nicht ohne Tricks mit einer Textvariable zugreifen,…


Hier dachte ich mir … mal sehen, ob ich den HttpContent auch ohne Umweg über eine Text-Variable füllen kann.
Content.WriteFrom() nimmt auch eine InStream entgegen.

Also nun:
FileManagement.BLOBImportFromServerFile(TempBlob, FilePath);
TempBlob.CreateInStream(ContentInStream);
Content.WriteFrom(ContentInStream);


und das sende ich dann ab.
Tada. Es hat funktioniert.

Da ich den Umweg des InStream über ein BLOB (TempBlob) dennoch als überflüssig erachte habe ich nun noch folgendes probiert.

ImageFile.Open(FilePath);
ImageFile.CreateInStream(ContentInStream);
Content.WriteFrom(ContentInStream);


Und ab dafür.
Auch das geht hervorragend und ganz ohne Umweg über ein Blob/TempBlob.
Also ist die Text-Variable wohl der limitierende Faktor. Ich vermute hier wird zu viel weggeschnitten.

Danke für deine nette und freundliche Diskusion.

Edit:
Ich habe es gerade ausprobiert indem ich den InStream in Base64 codire und direkt in den Content schreiben. Das geht nicht. Die Datei kommt zwar in Shopware an. Ist aber nicht lesbar. Somit kann ich den Forembeitrag bestätigen, dass Shopware aktuell noch kein Base64 unterstützt. Also bleibt die Lösung wie oben beschrieben :)

Re: Upload Image als Binary

5. August 2022 13:46

Hallo,

schau mal was da in der Beschreibung steht:

Code:
{
  "url": "<[b][u][color=#FF4000]url-to-your-image[/color][/u][/b]>"
}


Shopware möchte ein URL haben. D.h. etwas, was du in jedem Browser aufrufen kannst. Das funktioniert nicht mit NAV/bzw. BC.

Was du als erstes mal benötigst, ist ein "Dateigrab" im Internet, auf das du - mit was auch immer - zugreifen kannst, um dort Dateien hochzuladen und auf der anderen Seite muss es dieser Dienst erlauben, diese Dateien mit einem ganz normalen Browser zu öffnen, wie in meinem Beispiel.
Dein Problem dürfte es jetzt sein, dieses "mit was auch immer" in BC zu implementieren, ohne DotNet zu benutzen. Mit der WinScp DotNet- Komponente wäre das kein großes Problem.

Es gibt übrigens Addons, die eine Shop-Anbindung für euch handeln können, da ist noch einiges mehr zu beachten. 8-)

Gruß Fiddi

Re: Upload Image als Binary

5. August 2022 14:04

fiddi hat geschrieben:Hallo,

schau mal was da in der Beschreibung steht:

Code: Alles auswählen
{
"url": "<url-to-your-image>"
}


Das stimmt nicht. Du schaust unter dem Unterpunkt "Provide a resource URL" ein kleines stück tiefer ist der nächste Unterpunkt "Upload the resource directly".
Genau weil ich kein Dateigrab möchte sollte es ja direkt "Upload the resource directly" funktionieren. Und das tut es jetzt ja auch.

Aquator hat geschrieben:Es gibt übrigens Addons, die eine Shop-Anbindung für euch handeln können, da ist noch einiges mehr zu beachten.

Da hast du recht. Die gibt es. Alle wo wir angefragt haben winkten bei unseren Anforderungen ab. Wir hätten zu viele andere Erweiterungen und aus diesen könne man die Felder nicht mit abdecken. Es werden zumeist nur Standardfelder in den fertigne Shop-Anbindungen beachtet und behandelt. Daher müssen wir die nun selbst bauen. Und das mit dem beachten von dingen... das ist ja nunmal in der Programmierung so. Das bleibt nicht aus.

Am Ende verstehe ich deinen letzt Beitrag auch eigentlich nicht. Warum du das schreibst. Ich habe ja bereits geschrieben das es funktioniert. :-o

Re: Upload Image als Binary

5. August 2022 15:17

Hallo,

Das stimmt nicht. Du schaust unter dem Unterpunkt "Provide a resource URL" ein kleines stück tiefer ist der nächste Unterpunkt "Upload the resource directly".

Da hast du recht, das habe ich übersehen.

Schau dir mal die Codeunit HttpWebRequestMgt.Codeunit.al an. Da gibt es eine Funktion "AddBodyBlob". Das sollte - ich hab's nicht ausprobiert - dir helfen. Aber das ist "[Scope('OnPrem')]". D.h. solltet ihr keine Lizenz haben, die vor dem 1.4.22 oder 1.5.22 dieses Jahr auf BC umgestellt wurde, dann wird das teuer, weil die Software nicht "CloudReady" ist.

Viele Felder sollten eigentlich kein Problem sein, wenn Sie denn nicht nur über ganz üble Umwege ermittelt werden können. Eine gute Shop- Schnittstelle beherrscht Attribute, mit denen man nahezu beliebig viele Felder transferieren kann. Problematisch wird das nur dann, wenn man beim Upload der Artikelstammdaten, die Feldinhalte nicht einfach ermitteln kann.
Aber hier liegt der Teufel, wie meistens, im Detail.

Gruß Fiddi