Join us for an upcoming Shopify Partner webinar on February 27, 2024. Discover the latest Checkout Extensibility features, and deep dive on improvements to Shopify Functions and Web Pixels. Register now for either the 10am EST or 2pm EST sessions.

PDF upload to Settings > Files; PDF Error: Failed to load PDF document.

mstewe
Shopify Partner
1 0 0

Hello everyone 👋


I'm currently stuck trying to upload a PDF to the admin settings > files area. I'm able to upload the PDF but when I try to click on it in the admin, I get the following error modal.

Screenshot 2022-12-28 at 12.41.55 PM.png

Here are the steps I took so far.

 

I'm sending the data returned from the form <input type="file" name="file"> via a POST request to my custom app, which is a WordPress site. Because the app's "Distribution model" is "Shopify admin", I'm only authenticating via the access token.

 

Here's rough outline of the JS code that runs on the submit event of the form.

 

 

 

const formData = new FormData(e.target)
const formProps = Object.fromEntries(formData)
async function readFileAsync(blob, method = 'readAsDataURL') {
    return new Promise((resolve, reject) => {
        let reader = new FileReader
        reader.onload = () => resolve(reader.result)
        reader.onerror = reject
        reader[method](blob)
    })
}
const fileDataUrl = await readFileAsync(formProps.file)
formData.append('fileDataUrl', fileDataUrl)
// Hereafter, the formData is send via a POST request using Axios.

 

 

 

 

On the WordPress/PHP side I then continue to call stagedUploadsCreate and send its query parameters plus fileDataUrl (created via JS above) to the URL provided by the response. Then, I call the fileCreate mutation which, again, does upload the file, but the file doesn't work. My suspicion is that I'm not supplying the correct data to 

$file_upload_body['file'].

 

 

 

// NOTE $request is an instance of WP_REST_Request.
$file_data_url = $request->get_param('fileDataUrl');
$file = $request->get_file_params();
$file = $file['file'];
$query = <<<QUERY
    mutation stagedUploadsCreate(\$input: [StagedUploadInput!]!) {
        stagedUploadsCreate(input: \$input) {
            stagedTargets {
                url
                resourceUrl
                parameters {
                    name
                    value
                }
            }
            userErrors {
                field
                message
            }
        }
    }
QUERY;
$input_data = [
    [
        'fileSize' => $file['size'],
        'filename' => $file['name'],
        'httpMethod' => 'PUT',
        'mimeType' => 'application/pdf',
        'resource' => 'FILE',
    ],
];
// This function is just a small wrapper around `wp_safe_remote_request`.
$res = send_shopify_admin_api_mutation( $query, [ 'input' => $input_data ] );

$staged_targets_url = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->url;
$staged_targets_resource_url = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->resourceUrl;
$staged_targets_parameters = $res['body']->data->stagedUploadsCreate->stagedTargets[0]->parameters;

$file_upload_body = [];
foreach ( $staged_targets_parameters as $param ) {
    $file_upload_body[$param->name] = $param->value;
}
$file_upload_body['file'] = $file_data_url;

$file_upload_res = wp_safe_remote_request(
    $staged_targets_url,
    [
        'method' => 'PUT',
        'body' => $file_upload_body,
    ]
);

$query = <<<QUERY
    mutation fileCreate(\$files: [FileCreateInput!]!) {
        fileCreate(files: \$files) {
            files {
                alt
                createdAt
                fileStatus
                fileErrors {
                    code
                    details
                    message
                }
            }
            userErrors {
                field
                message
            }
        }
    }
QUERY;
$input_data = [
    [
        'contentType' => 'FILE',
        'originalSource' => $staged_targets_resource_url,
    ],
];
$file_create_res = send_shopify_admin_api_mutation( $query, [ 'files' => $input_data ] );

 

 

 

And here are the responses I get for the HTTP requests in the above PHP.

 

// Response for stagedUploadsCreate:
{
    "code": 200,
    "success": true,
    "message": "OK",
    "body": {
        "data": {
            "stagedUploadsCreate": {
                "stagedTargets": [
                    {
                        "url": "https://shopify-staged-uploads.storage.googleapis.com/tmp/xxxxxxxxxxx/files/xxxx-xxxxx-xxxxx-x-xxx-xxx/test.pdf?X-Goog-Algorithm=&X-Goog-Credential=&...",
                        "resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/xxxxxxxxxxx/files/xxxx-xxxxx-xxxxx-x-xxx-xxx/test.pdf",
                        "parameters": [
                            {
                                "name": "content_type",
                                "value": "application/pdf"
                            },
                            {
                                "name": "acl",
                                "value": "private"
                            }
                        ]
                    }
                ],
                "userErrors": []
            }
        }
    },
    "cookies": [],
    "headers": {}
}
// Response for the $file_upload_res variable.
// If I run the stagedUploadsCreate via the POST method vs. the PUT, I get a 503 "Service Unavailable" response.
{
    "code": 200,
    "success": true,
    "message": "OK",
    "body": "",
    "cookies": [],
    "headers": {}
}
// Response for the fileCreate mutation.

{
    "code": 200,
    "success": true,
    "message": "OK",
    "body": {
        "data": {
            "fileCreate": {
                "files": [
                    {
                        "alt": "",
                        "createdAt": "2022-12-28T14:38:35Z",
                        "fileStatus": "UPLOADED",
                        "fileErrors": []
                    }
                ],
                "userErrors": []
            }
        }
    },
    "cookies": [],
    "headers": {}
}

 

 

Again, I think that I'm not supplying the right value to $file_upload_body['file']. I couldn't find any documentation on that.

 

Thanks for any help in advance.

Reply 1 (1)

ShopifyDevSup
Shopify Staff
1244 201 441

Hi @mstewe👋

 

To create a staged target for a PDF file, we need to specify all the fields of the `StagedUploadInput` in the payload like the below:

 

{ 
    "input": {
        "resource": "FILE",
        "filename": "test.pdf",
        "mimeType": "application/pdf",
        "httpMethod": "POST",
        "fileSize": "12826"
    }
}

 

This way, the returned parameters array will include a "key" that is required in the form data when when uploading the file to the signed URL.

 

{
    "data": {
        "stagedUploadsCreate": {
            "stagedTargets": [
                {
                    "url": "https://shopify-staged-uploads.storage.googleapis.com/",
                    "resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/55025631350/files/.../test.pdf",
                    "parameters": [
                        {
                            "name": "key",
                            "value": "tmp/55025631350/files/.../test.pdf"
                        }, ...

 

Hope that helps!
 

Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog