PDF Media Upload über API

Hallo zusammen,

ich möchte ein PDF hochladen, erzeuge dazu erst eine mediaId. Die PDF-Daten habe ich in einem base64Encoded String. Ich schaffe es aber nicht, die Daten hochzuladen.

Ich erhalte immer das folgende Result:
{„errors“:[{„status“:„404“,„code“:„CONTENT__MEDIA_NOT_FOUND“,„title“:„Not Found“,„detail“:„Media for id 1e23680b921641a9bc3804dd12e07889 not found.“,„meta“:{„parameters“:{„mediaId“:„1e23680b921641a9bc3804dd12e07889“}}}]}

Call zu Shopware wäre der hier:
/api/_action/media/[myMediaID]/upload?extension=pdf&fileName=12345

Im Body soll dann sicher der base64Encoded String mitgegeben werden.

Fragen dazu:
Welche Header muss ich mitgeben ? Muss ich explizit einen Content-Type mitgeben und wenn ja, welchen ? Wie muss der Body mitgegeben werden ?

Hat jemand das schon mal gemacht und vielleicht ein Beispiel dazu für mich ?

Besten Dank und Grüße
Holger

Fast :slight_smile:

  1. Die Daten müssten NICHT als base64 sondern als octet-stream gesendet werden. Hängt dann vom Client wie man das macht.
curl ... --data-binary @/var/www/temp/zxy.pdf

(die Magie kommt hier von dem @-Zeichen !)

Andere Client machen das anders.

  1. Ich bin mir gerade nicht 100% sicher ob man PDF Dateien auch als media-file ablegen kann. Ich denke schon. Aber jeden Fall werden die Dateien kann nicht angezeigt. Ist aber alles ein anderes Problem.

kleiner Ausschnitt aus einem PHP Script was den den Upload macht:

$url  = $this->shopUrl . '/api/_action/media/' . $mediaId . '/upload?';
$url .= 'extension=' . $pi['extension'];
$url .= '&fileName=' . urlencode($pi['filename']);

$content_type = $storage->mimeType($asset->getRealFullPath()); 

// $tmpfname = tempnam("/tmp", "pic." . $pi['extension']);
$asset->getData();

$headers = array(
    "Content-Type: $content_type", // or whatever you want
    "Authorization: " . $this->getAuthHeader()
);

$filesize = $storage->fileSize($asset->getRealFullPath());
$stream = $asset->getStream();

$curl_opts = array(
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_PUT => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_INFILE => $stream,
    CURLOPT_VERBOSE => 0,
    CURLOPT_INFILESIZE => $filesize,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
);

(Achtung das ist nicht komplett, da fehlt das finale CURL-exec und die Daten kommen hier aus einem PIM; $asset object, lässt sicher aber sicher auch in PHP abbilden.)

LG
Carsten

Hallo chamaw,

die Doku sagt dazu das hier, was ich aber nicht wirklich verstehe

curl --request POST \
  --url 'http://localhost/api/_action/media/0fa91ce3e96a4bc2be4bd9ce752c3425/upload?extension=jpg' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "// binary file body": null
}'

Was übergebe ich denn als binary file body, einen Stream kann ich ja nicht schicken… und du sagtest NICHT als base64… ich bin ratlos, weil es bei mir mit allen möglichen Varianten und Clients nicht funktioniert. Letzter Versuch war mit Indy und TIdMultiPartFormDataStream… da gefällt dem Server die content-lenght nicht… PHP hilft mir nicht viel weiter.

Grüße
Holger

auf der CMD line dann einfach

curl ... --data-binary @/pfad/auf/dem/server/asdf.pdf

vorne das @ nicht vergessen, dann packt curl den Inhalt der Datei in den Body.
content-type ist dann „application/pdf“ und der Bearer Token müsste auch gesetzt werden …

Ich muss gestehen, ich habe keine Ahnung, was Du meinst.
CMD Line ? Ich habe eine Delphi Anwendung, mit der ich versuche, ein PDF hochzuladen.
Es kann doch nicht sein, dass ich der erste bin, der damit Probleme hat. Ich finde hier im Forum leider nichts dazu.

Tatsächlich gehört Delphi nicht zu meinem Portfolio. Hier aber ein CodeStück aus C#, vielleicht hilft das:

HttpClient httpClient = new HttpClient();

httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/vnd.api+json"));

httpClient.DefaultRequestHeaders.Add("Authorization", this.AuthHeader);

byte[] rawBytes = File.ReadAllBytes(filepath);
if (rawBytes.Length == 0)
{
    throw new Exception("File is empty " + filepath);
}
ByteArrayContent byteArrayContent = new ByteArrayContent(rawBytes);
byteArrayContent.Headers.ContentType = new MediaTypeHeaderValue("image/" + mediaUpload.Extension.ToLower());
byteArrayContent.Headers.ContentLength = rawBytes.LongLength;

string finalSendDataUrl = this._config.server.TrimEnd('/') + path;
HttpResponseMessage result = httpClient.PostAsync(finalSendDataUrl, byteArrayContent).Result;

Wichtig ist halt, dass das Image am Ende einfach „raw binary“ im Body landet.

Vielleicht hilft dir das:

Ich würd einfach das SDK benutzen.

Ich will das Thema nochmal aufmachen.
Ich habe immer noch Problem damit, über die API ein PDF in die Medien zu bekommen.
Ich kann aber über eine ähnliche Funktion, zum Beispiel zu einer Order ein PDF als Lieferschein zuweisen. Letztlich nutze ich fast die gleiche Funktion, nur ein anderen Endpunkt.

Erst mal das, was nicht funktioniert. Meine Funktion in Delphi:

FileStream := TFileStream.Create('J:\Rechnung_151866047.pdf', fmOpenRead);
    try
      SetLength(ByteArrayContent, FileStream.Size);
      FileStream.ReadBuffer(ByteArrayContent[0], FileStream.Size);
    finally
      FileStream.Free;
    end;
    ByteStream := TBytesStream.Create(ByteArrayContent);

    myRESTRequest.ResetToDefaults;
    myRESTRequest.ClearBody;
    myRESTRequest.Timeout := 300000;
    myRESTClient.ResetToDefaults;
    myRESTResponse.ResetToDefaults;
    myRESTClient.BaseURL := 'https://meinedomain';
    myRESTClient.Params.Clear;

    myRESTRequest.AddAuthParameter('Authorization', 'Bearer ' + my_access_token, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]);
    myRESTRequest.Method := TRESTRequestMethod.rmPOST;
    myRESTRequest.Resource := '/api/_action/media/'+myMediaID+'/upload?extension=pdf&fileName='+TNetEncoding.URL.Encode('Rechnung_151866047');
    Memo1.Lines.Add(myRESTRequest.Resource);
    myRESTRequest.AddBody(ByteStream, TRESTContentType.ctAPPLICATION_OCTET_STREAM);

    try
      myRESTRequest.Execute;
    except
      on e: exception do
      begin
        Memo1.Lines.Add('Execute: '+e.message);
      end;
    end;

Als Result erhalte ich immer:

{
   "errors":[
      {
         "status":"404",
         "code":"CONTENT__MEDIA_NOT_FOUND",
         "title":"Not Found",
         "detail":"Media for id 229250bb7b564d5fa0a9abe3a2f2d6d6 not found.",
         "meta":{
            "parameters":{
               "mediaId":"229250bb7b564d5fa0a9abe3a2f2d6d6"
            }
         }
      }
   ]
}

Ich habe eine ähnliche Funktion, die ein Dokument zu einer Order schickt. Und die funktioniert…
Ich habe keine Idee, warum das eine geht und das andere nicht.

    FileStream := TFileStream.Create('S:\12345.pdf', fmOpenRead);
    try
      SetLength(ByteArrayContent, FileStream.Size);
      FileStream.ReadBuffer(ByteArrayContent[0], FileStream.Size);
    finally
      FileStream.Free;
    end;
    ByteStream := TBytesStream.Create(ByteArrayContent);

    myRESTRequest.ResetToDefaults;
    myRESTRequest.Timeout := 300000;
    myRESTClient.ResetToDefaults;
    myRESTResponse.ResetToDefaults;
    myRESTClient.BaseURL := BaseURL;
    myRESTClient.Params.Clear;
    myRESTRequest.AddAuthParameter('Authorization', 'Bearer ' + my_access_token, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]);
    myRESTRequest.Method := TRESTRequestMethod.rmPOST;
    myRESTRequest.Resource := '/api/_action/document/'+myDocumentID+'/upload?fileName=12345&extension=pdf';
    Memo1.Lines.Add(myRESTRequest.Resource);
    myRESTRequest.AddBody(ByteStream, TRESTContentType.ctAPPLICATION_OCTET_STREAM);

    try
      myRESTRequest.Execute;
    except on E: Exception do
      begin
        Memo1.Lines.Add(e.message);
      end;
    end;

Hat vielleicht noch jemand eine Idee, was das Problem ist ?

Grüße Holger

Da gibt es schon einen großen Unterschied!
Kurz zur Technik, es gibt das DB-Objekt Document (also die Rechnung oder einen Lieferschein) und letztlich ist das nur ein Zwischen-Objekte, das dann die Bestellung und ein Media Object verbindet. Am Ende hast Du dann die DocumentId, eine MediaId und die OrderId.
Und auch der Media Datensatz ist ledig ein Datenbankeintrag. Das Bild oder bei den Dokumenten die PDF Datei liegt immer im Filesystem. Und „am Anfang“ ist das halt ein (noch) leerer Eintrag. Aber die MediaId und den zugehörenden media satz gibt es schon !

Der Upload wechselt den binären Inhalt aus (auch von Leer auf Datei). ABER der Upload für den Media-Eintrag geht davon aus, dass es den Datenbank Eintrag schon gibt. Sprich hier müsstest Du zuerst den media-eintrag erzeugen und erst danach den Upload machen.

Der zweite Code-Snippet macht dann aber den Upload auf dem Document. Ich habe den Shopware Code nicht im Kopf ich vermute aber, dass hier zunächst die DB Objekte erzeugt werden …

Danke für deine super schnelle Antwort.
Ich hole mir natürlich zunächst eine mediaID über einen Call zu /api/media unter Angabe der folderID. Damit wird dann das zunächst leere Objekt angelegt.
Und diese mediaID gebe ich dann ja auch in meinem Call mit…
Man sieht ja im Result, dass es die gibt.

ja aber einmal:

und dann

Ich verstehe nicht, was Du mir mit dem letzten Post sagen willst.
Ich glaube aber sowieso, dass ich herausgefunden habe, warum es nicht funktioniert hat.
Letztlich will ich Rechnungsdaten hochladen und dann zu einem Plugin (VIOSYS Kundendokumente) zuweisen, welches Kundendokumente darstellt. Und wenn ich das im Backend tue, dann lege ich die mediaID mit „private=true“ an.
Mache ich das in meinen Calls über die API, dann funktioniert der Upload und die Zuweisung zur mediaID nicht mehr. Wenn ich private auf false setze, funktioniert es sofort.