Azure Logic Apps: PDF files get corrupted when sent via http WebAPI file upload

1.4k views Asked by At

I'm using logic apps to call the Custom Web API to upload PDFs that are stored on blob storage. I can call the endpoint directly via postman and everything works fine - i.e. upload a PDF file via postman, file gets saved correctly and I can open the file ok.

When I try to replicate the request in Logic Apps, it looks like everything is working fine - meaning blob storage PDF files gets uploaded fine and saved correctly via webapi but they are corrupted somehow - ADOBE reader error message. When I compared the original PDF file - open in notepad and WebAPI saved/uploaded PDF file (open in notepad), I do find some different symbols in PDF which makes the uploaded file corrupted (see ???? texts in body).

Any thoughts on what is causing this issue and how to resolve it?

WebAPI Request Body:

{ "$content-type": "multipart/form-data", "$multipart": [ { "body": "%PDF-1.5\n%����\n1 0 obj\n<</Type/Font/Subtype/Type1/BaseFont/Times-Roman/Encoding/WinAnsiEncoding>>\nendobj\n2 0 obj\n<</Type/Font/Subtype/Type1/BaseFont/Times-Bold/Encoding/WinAnsiEncoding>>\nendobj\n3 0 obj\n<</Type/Page/Contents 4 0 R/Resources<</Font<</F3 5 0 R/F2 2 0 R/F1 1 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Parent 6 0 R/MediaBox[0 0 612 792]>>\nendobj\n5 0 obj\n<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding>>\nendobj\n4 0 obj\n<</Length 736/Filter/FlateDecode>>stream\nx���]O�0\u0014���+�%H���\u001d�J\u001b\n\u001ak��۸@BYkP�&)I�Ŀ�S>\u0006R�tZo�X����>>�<Dg& b\nf�\u001ep�78g@��]tD�ͯ���D_���\u001b�t��P�G\u0018I��wD0C\u0002\u0014��q("�\u0015�o��h�>w���g��\u0014\u0003\u0019K�\u0014���J��n�7��e�lk�:}Vq�IxG T!҇Hg��\u0017��<Iaa��t\u0002�f�,\u0016'p9\u001d�\u0007�X�@�ܳm���i<�8�H�\u001e��QL��1P)?\t��~�\u0012�j��!\u001d�H��غ}�q�Pn�{2�\u001c%�SIb�,�\u0011�\u0016�&�\u0014����\t�kP�+�\u001f���\u0013\u0012i�]T��,��?F�\u0019b^��I��\u0004I\bR�G\u001b�����nPO|A�8+s��Imm��!��#\^w7ݣ�6YٓBaİ�un�m�:1�v\u0013\u0014#�%�&PҴy��U�#'\b9�\u0017�\u0011%��K\u0010q�/\ L@P��Kɠ���n���k\r\u0011H�\u000f�=\u0005#\u001d\u001b�ݏi\n��'Qz��y�ΗOaI4>@�h\u000eXKʅ�dl|�8�H�\u001eM�ic�����_�]YUq��\u001fy��qVd��G� <v\u0015#p;[\u000b��l�e����$�\u0007z@1��\u0019Bw-��ƪuPLؙW)A\u000e\u001981~)ԕug�\u0001ZR�����\u0017&I?��\u0017�O�%\u001c~���\u0011v�H�h���ر��â1��\u0001��l��y\r.��\u0010��\u0006Gݼ�P�vg\u0017�Դ\b������\u0018ĽWƱW�rA���\u0004bo��:Z�ʛ�+(�A�닙�����/)w\fB��2R۸���yU\u000e\u0016�~t�\u0006�-��]{b�\u0015#�1�ަ����\u0013L�\u0003�;��>���~\u0012�1"�G:_T�\u0000域�\nendstream\nendobj\n6 0 obj\n<</Type/Pages/Count 3/Kids[3 0 R 7 0 R 8 0 R]/ITXT(4.1.6)>>\nendobj\n7 0 obj\n<</Type/Page/Contents 9 0 R/Resources<</Font<</F3 5 0 R/F2 2 0 R/F1 1 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Parent 6 0 R/MediaBox[0 0 612 792]>>\nendobj\n9 0 obj\n<</Length 494/Filter/FlateDecode>>stream\nx���Oo�@\u0010���\u0014s�Hհ;޿��Bh�*\u0010pn�"���\u0015��v���5�\u0004*��IN�˾��μ�c�)�\u0018hK���\u0017�|\f�b�n�G4����q{�D��q\u000e�\u0006\u0007�@\u0012���p\u0012\u000e�5\u0012���\G\u0003ΆfH�X�X7�\u0013��\u0012��#��:\u001b\u0001u\u0003H\n$�B��-���s��i�����7\�Y�u7(�\u0017�Ɂ�9tΙTu^��+f1����l>�,\u001e��bv3}��\u001e�\u0002e�A\u001f�����J�Ә@\u0015���m���n��ZTq\u0000���t\u0003�uVT�aqB+\u0003�c��;\u0001=\u001e\u000e-�/��k�SԶ^\u0019���\"�Z���z\u0013-�\u001d�tTKd�������z�\r��\u00170N�y��i�e��Wq+P\u0004�\u0001\u0017Sl�P�Q�|\u000e&���\n\u0010d�d�!<S�y@����Rs�����I�1�fg�UU��²N��Y�{�\nkH˚\u0016=[�4�9���q\u0017|\u001d����=rA�B�'y�\u001f�\u0019��g>�)oΧt\u001e��d\b�s\b�Py��ߔ�~d(����2�\u001d�jDƃ0�\n\u001b@x#n]�t�~�F��&�\u001f�O8���\u0019�\u000b\n�q\nendstream\nendobj\n8 0 obj\n<</Type/Page/Contents 10 0 R/Resources<</Font<</F3 5 0 R/F2 2 0 R/F1 1 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Parent 6 0 R/MediaBox[0 0 612 792]>>\nendobj\n10 0 obj\n<</Length 798/Filter/FlateDecode>>stream\nx���[s�@\u0018���\u0015{�̘�κ\u0007��w&\u001a�i�XC��P�X2\b\u0006ȡ��\u000b�I��-�\\�\u000b�����A\u001f���\"��\u0019\nbuB�C��#�\u001a�\u001e?\u000f���q��o��n����\t�z(X�\bE��\u0011�\-�V����g��\rcY\u0003�ą\u0010�y0b\u0016.�\u0000��\u0000fs��ȵ���\u0010\u0018��{¸��UXI4|��\n\u0005E��\u001a��]�\�h;\u0018;\f\u001b����/׳�x~{\u0013̯���+\na\u001d��\u000f�r�An^\u001cFP�b�\u0019\u0018#կ~��$���չ�7���6�.�2ɲ$[�Y�D\u001a\u0012�}�8�\u0007�b\u0003��\u0004SS�����0[�\u0010�\u0012�\u001a\u0010Ӽ������|�z\u0013Q���\u0005�w���g7���Ժ�\u001f\u0004�ݮ���,^�F����w<Y��Q�%R�[h��\u001bV��\u0003�\u000f�f|q±���m�!X����,\�}\rîm30��!\V0�\u001b�U^�)�\u001aM\u0016�rq\u000e�F��\u000e�1'��\u0019\u0018\u0017\u00043�a�\u0006i��枦g\u001e���2�k[�\u0018�ðozE�:��z�^��HƏQ��Ju3ݦTܯ���+��\u0004�/zQ�$\u000b�H�r-��.�\f���Պw�9 m�\u001c[�@m�\u0001Y\u001d�9�њ\u0003B���̡\u001c;�\u0004s���\u0017FE^�(Lլ���0k\t߯��\u000f�\u0007����&\u0003�ۃ5���n����\u0003CL��� |A��ERƉ�'�P)+�� \u0007�E�<L���\b�3q\u0006~�˱\u000b8x�1 m�\u0018�ƀ��Ɯ�h�\u0001!Fc���Y�i���6�\b�#G�S��*~�5��]��TS��,A��C\t��4n\u001fD�B�\u000f�\u000e�\u001d�\u001d���I�V;\u0010b�\u000eL���e�$�b[�\u0012�\u0018s0�n�;�\u0007�Wa,\rK\u001b�X;�\u001d-\f\u0004��\u0017\u001f�\u0005Bu��\u0014Dk\u000b�0�\u0002�]i\u00163�F�`G�)��\b����:�����\u000bE��~\nendstream\nendobj\n11 0 obj\n[3 0 R/XYZ 0 804 0]\nendobj\n12 0 obj\n[7 0 R/XYZ 0 804 0]\nendobj\n13 0 obj\n[8 0 R/XYZ 0 804 0]\nendobj\n14 0 obj\n<</Names[(1) 11 0 R(2) 12 0 R(3) 13 0 R]>>\nendobj\n15 0 obj\n<</Dests 14 0 R>>\nendobj\n16 0 obj\n<</Type/Catalog/Pages 6 0 R/Names 15 0 R>>\nendobj\n17 0 obj\n<</Title(Estimate Audit)/Creator(Xactimate 28.300.20080.36805)/Producer(iTextSharp 4.1.6 by 1T3XT)/CreationDate(D:20201008144100+00'00')/ModDate(D:20201008084230-06'00')>>\nendobj\nxref\n0 18\n0000000000 65535 f \n0000000015 00000 n \n0000000105 00000 n \n0000000194 00000 n \n0000000452 00000 n \n0000000364 00000 n \n0000001255 00000 n \n0000001330 00000 n \n0000002061 00000 n \n0000001500 00000 n \n0000002232 00000 n \n0000003098 00000 n \n0000003134 00000 n \n0000003170 00000 n \n0000003206 00000 n \n0000003265 00000 n \n0000003299 00000 n \n0000003358 00000 n \ntrailer\n<</Size 18/Info 17 0 R/ID [<7b2a85db783c2dfe11911d8bdba318eb><4e585c7bf06c8a42fc07c328cf0f0f89>]/Root 16 0 R>>\nstartxref\n3546\n%%EOF\n", "headers": { "Content-Disposition": "form-data; name="file"; filename="/imagerightextract/Xactimate-Export-File-1.zip/ESTIMATE_AUDIT_REPORT.PDF"" } } ] }

Additional Details below in the images.

Logic App Snapshot

Logic App run-time snapshot

3

There are 3 answers

0
jfoster67 On

I had the same issue, essentially, and was able to get it to work.

I configured my Logic App as recommended in the following article:

Call service endpoints over HTTP or HTTPS from Azure Logic Apps

Which is really just using json to construct the entire body. I then used base64ToBinary on the contentBytes of the attachments because I noticed in my investigation that the following code produced a readable file:

// Reads a text file with the base64 string I pulled from an Azure Logic App "inputs"
string requestBody = ReturnContent();
byte[] encodedBytes = Convert.FromBase64String(requestBody);
File.WriteAllBytes("C:\\<your_path>\\test.pdf", encodedBytes);

Once I did this the Logic App produced a readable file.

My HTTP Action:

enter image description here

Just note that where you see "Attachments Name" that in the code I'm using base64ToBinary.

"body": "@base64ToBinary(items('For_each')?['contentBytes'])"

I think it's somewhat important to note that the Content-Type in the multipart section has been omitted. If you look at the actual request, it goes over as application/octet-stream, which you're not going to want to overwrite... You also don't have to specify the Content-Type of the overall request... Logic Apps figures this out as well.

The only other thing that I had to overcome was the possibility of filenames that had spaces and other normally url-encoded characters... so, I'd suggest that you specify the file name in the body as follows:

"headers": {
   "Content-Disposition": "form-data;name=fileData; filename=@{encodeURIComponent(items('For_each')?['name'])}"
}

Also of note is that my Controller method (Asp.Net Core) has an IFormFile parameter... its signature looks like this:

[HttpPost("{cid}")]
[ProducesResponseType(typeof(bool), (int)HttpStatusCode.OK)]
public async Task<IActionResult> ProcessAttachment(string cid, IFormFile fileData)

I hope this helps someone else.

0
Gareth On

I've been struggling with this one an don't have a definitive but what you can try is to use base64 where you convert your file content to base64. I have had the most success by inserting this directly in the code.

0
Jan Hajek On

We solved it this way and it works flawlessly. I suppose, you should replace the file body with just body('Get_file_content_2'), however we found this to work the best.

{
  "$content-type": "multipart/form-data",
  "$multipart": [
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"Task\"",
        "Content-Type": "application/json"
      },
      "body": {
        "name": "test"
      }
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"Data\"; filename=\"test.pdf\"",
        "Content-Type": "application/pdf"
      },
      "body": {
        "$content-type": "application/pdf",
        "$content": "@{body('Get_file_content_2')?['$content']}"
      }
    }
  ]
}

enter image description here enter image description here