Out now! Check out the Poll results: Do you have a Shopify store?
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Upload File pdf API

Upload File pdf API

MarcoIngShop
Shopify Partner
17 0 3

Hi all. I'm using a Custom Data field to allow to upload a PDF file each product on their Shopify store. The file is going to be downloadable from each product page.

 

I would like to write a function in C# to upload files via the API but I can't understand what the correct procedure is.

 

This is my code example (it doesn't work):

 

        public async Task UploadFile(byte[] fileContent, string fileName)
        {
            using (var client = new HttpClient())
            {
                var requestUri = $"https://{_shopifyStoreUrl}/admin/api/2023-10/files.json";
                client.DefaultRequestHeaders.Add("X-Shopify-Access-Token", _accessToken);

                var content = new MultipartFormDataContent();
                var fileContentContent = new ByteArrayContent(fileContent);
                fileContentContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                {
                    Name = "\"file\"",
                    FileName = $"\"{fileName}\""
                };
                content.Add(fileContentContent);

                var response = await client.PostAsync(requestUri, content);
                response.EnsureSuccessStatusCode();
            }
        }

Can you help me understand where I'm wrong?

 

The response is: "StatusCode: 406, ReasonPhrase: 'Not Acceptable'"

 

Thanks a lot.

Replies 9 (9)

SBD_
Shopify Staff
1831 273 421

Hey @MarcoIngShop,

 

I can't speak to the C# code specifically but can point you in the right direction. I don't think there's a `/files` endpoint - but you should be able to create a file with this (GraphQL) mutation.

Scott | Developer Advocate @ Shopify 

MarcoIngShop
Shopify Partner
17 0 3

Thank you for your suggestion. I understood that the operation must be done in 2 steps.

 

The first is GenerateUploadUrl and then upload. 

 

I think I managed to do the first step but then it fails when I send the file.

 

I have upload url and parameters.

 

Could you help me understand where the error is in the second step?

 

This is my code:

public async Task<string> GenerateUploadUrlAndUploadFile(string fileName, byte[] fileContent)
{
    var uploadDetails = await GenerateUploadUrl(fileName, fileContent.Length);
    if (string.IsNullOrEmpty(uploadDetails))
    {
        throw new Exception("Unable to generate upload URL.");
    }

    // Estrai i dettagli di caricamento dalla risposta
    var uploadData = JsonConvert.DeserializeObject<JObject>(uploadDetails);
    var uploadUrl = uploadData["data"]["stagedUploadsCreate"]["stagedTargets"][0]["url"].ToString();
    var uploadParameters = uploadData["data"]["stagedUploadsCreate"]["stagedTargets"][0]["parameters"].ToObject<List<Dictionary<string, string>>>();


    // Prepara la richiesta di caricamento
    var requestMessage = new HttpRequestMessage(HttpMethod.Post, uploadUrl)
    {
        Content = new ByteArrayContent(fileContent)
    };

    // Imposta i parametri come header della richiesta
    foreach (var parameter in uploadParameters)
    {
        requestMessage.Headers.TryAddWithoutValidation(parameter["name"], parameter["value"]);
    }

    // Esegui la richiesta di caricamento
    var uploadResult = await _httpClient.SendAsync(requestMessage);
    if (!uploadResult.IsSuccessStatusCode)
    {
        Console.WriteLine("Failed to upload file. Response content: " + responseContent); 
        throw new Exception("Failed to upload file. Status: " + uploadResult.StatusCode);
    }

    // Restituisci l'URL della risorsa caricata o un'altra informazione rilevante
    return uploadResult.Headers.Location.ToString();
}

private async Task<string> GenerateUploadUrl(string fileName, long fileSize)
{
    var mutation = @"
    mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
        stagedUploadsCreate(input: $input) {
            stagedTargets {
                parameters {
                    name
                    value
                }
                url
                resourceUrl
            }
        }
    }";

    var variables = new
    {
        input = new[]
        {
        new
        {
            resource = "FILE", 
            filename = fileName,
            mimeType = "application/pdf",
            httpMethod = "POST",
            fileSize = fileSize.ToString()
        }
    }
    };

    var requestBody = new
    {
        query = mutation,
        variables = variables
    };

    var requestContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

    _httpClient.DefaultRequestHeaders.Clear();
    _httpClient.DefaultRequestHeaders.Add("X-Shopify-Access-Token", _accessToken);

    var response = await _httpClient.PostAsync($"https://{_shopifyStoreUrl}/admin/api/2023-10/graphql.json", requestContent);
    var responseString = await response.Content.ReadAsStringAsync();

    return response.IsSuccessStatusCode ? responseString : null;
}

 

 

SBD_
Shopify Staff
1831 273 421

Hey @MarcoIngShop 

 

Are you able to capture the request ID from the respond headers? This will let me examine the logs.

Scott | Developer Advocate @ Shopify 

MarcoIngShop
Shopify Partner
17 0 3

X-GUploader-UploadID --> ABPtcPqz1u45k2z5XfYw5nAdUtiIcSiOMp3mvHZ8spxt2_MB3PlIus6aMscZ73SIoVfc_tA1kxcXZCt7oQ

MarcoIngShop
Shopify Partner
17 0 3

I thik that this is the point "critical".

 

I would like to understand if I have to make a put or a post and if the parameters I send are correct (if they go in the header or in the body).

 

            //Load File
            var formContent = new MultipartFormDataContent();
            foreach (var parameter in uploadParameters)
            {
                formContent.Add(new StringContent(parameter["value"]), parameter["name"]);
            }
            string base64String = Convert.ToBase64String(fileContent);
            formContent.Add(new StringContent(base64String),"data");
            formContent.Add(new StringContent(fileName), "filename");
            //formContent.Add(new ByteArrayContent(fileContent), "data", fileName);
            //formContent.Add(new ByteArrayContent(fileContent),"data");
            //HttpContent httpContent = new HttpContent();

                      
            var uploadResult = await _httpClient.PostAsync(uploadUrl, formContent);
            if (!uploadResult.IsSuccessStatusCode)
            {
                throw new Exception("Failed to upload file.");
            }

 

SBD_
Shopify Staff
1831 273 421

I can't speak to the code directly, but this article breaks down the steps - maybe try running through this first: https://shopify.dev/docs/apps/online-store/media/products#generate-the-upload-url-and-parameters

 

It's specifically for product media, so difference would be:

- you would specify `FILE` for the `StagedUploadTargetGenerateUploadResource`

after uploading the file, you can add the file to the Files page in Shopify admin using the fileCreate mutation.

Scott | Developer Advocate @ Shopify 

MarcoIngShop
Shopify Partner
17 0 3

Hi,
This article is missing the example in "UPLOAD SECTION" of when a generic "FILE" type must be uploaded (in my case a PDF) so I can't understand if the request must be of type POST or PUT and where (and how) the parameters should be passed.

 

Can you tell me if for a generic FILE (pdf) type the request must be POST OR PUT and how the parameters should be passed?

 

In the first step I followed the instructions by putting the type "FILE" and I received the correct parameters but when it says to upload I tried various ways without success.

 

Thanks a lot.

SBD_
Shopify Staff
1831 273 421

Hey @MarcoIngShop 

 

Just tried it out - we could use better docs on the process 😅 I had success with the following method:

 

1. Generate a staged upload URL

mutation generateStagedUploads {
  stagedUploadsCreate(input: [
    {
      filename: "example.pdf",
      mimeType: "application/pdf",
      resource: FILE
    }
  ])
    {
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
    userErrors {
      field, message
    }
  }
}


2. Upload the file (A PUT request to `data.stagedUploadsCreate.stagedTargets.url` from step 1. If you want to test in Postman here's an example screenshot.

3. If you get a 200 OK from step 2, then you can use the `fileCreate` mutation to add the file to the 'files' section of the admin. You only need to pass the originalSource param, which is the `data.stagedUploadsCreate.stagedTargets.url` value from step one without any of the params.

{
  "files": [
    {
      "originalSource": "https://shopify-staged-uploads.storage.googleapis.com/tmp/.../example.pdf"
    }
  ]
}

 

Let me know how you go!

Scott | Developer Advocate @ Shopify 

MarcoIngShop
Shopify Partner
17 0 3

Can you check the log for this request:

 

X-GUploader-UploadID: ABPtcPqgQzIR9moKLvUInt0f35gTM7dK1NMGRDfIT2mEa1iEyUaGLDWVfniZHVTqTIeasppOz0s
Date: Thu, 23 Nov 2023 20:26:05 GMT
Server: UploadServer
Alt-Svc: h3=":443"; ma=2592000
Alt-Svc: h3-29=":443"; ma=2592000
Content-Type: text/plain; charset=utf-8
Content-Length: 25

 

Thanks a lot.