1

I'm trying to convert a base64 string to an image file and upload it into WordPress media library.

It seems like the code is working, but, for some reason, every time I call this function it uploads the same image multiple times.. I wonder what am I doing wrong and what I need to change in order to fix it.

This is my code:

<?php 

class Catalog
{

    public function uploadBase64Image()
    {
        //Base 64 string example
        $base64_img = include 'testbase64.php'; 

        $upload_dir  = wp_upload_dir();
        $upload_path = str_replace('/', DIRECTORY_SEPARATOR, $upload_dir['path']) . DIRECTORY_SEPARATOR;
        $title = "test"; 
        $image_parts = explode(";base64,",$base64_img);
        $decoded = base64_decode($image_parts[1]);
        $mime = substr($base64_img, 11, 4);
        $mime = str_replace(';', '', $mime);
        $filename = $title . '.' . $mime;
        $file_type = 'image/' . $mime;
        $hashed_filename = md5($filename . microtime()) . '_' . $filename;
        $upload_file = file_put_contents($upload_path . $hashed_filename, $decoded);

        $attachment = [
            'post_mime_type' => $file_type,
            'post_title' => preg_replace('/\.[^.]+$/', '', basename($hashed_filename)),
            'post_content' => '',
            'post_status' => 'inherit',
            'guid' => $upload_dir['url'] . '/' . basename($hashed_filename)
        ];

        $attach_id = wp_insert_attachment($attachment, $upload_dir['path'] . '/' . $hashed_filename);

        require_once ABSPATH . 'wp-admin/includes/image.php';
        $attach_data = wp_generate_attachment_metadata($attach_id, $upload_dir['path'] . '/' . $hashed_filename);
        wp_update_attachment_metadata($attach_id, $attach_data);

        return $attach_id;

    }

I'm including this class using require_once in my theme's functions.php file and using it this way:

 function testCatalog()
 {
    if(!is_admin()) {
        $catalog = new Catalog();
        return $catalog->uploadBase64Image();
    }
 }
 add_action('init', 'testCatalog');

Any help will be highly appreciated, thanks is advance!

2
  • base64 is irrelevant. This is happening because you're running it on the init hook, which runs any time WordPress is loaded, including AJAX or REST API requests. If you visit a page that sends AJAX requests you're going to get multiple uploads. Commented Jan 25 at 14:00
  • if you want to implement an AJAX upload you need to use an API such as the REST API to handle those requests, or you'll run into the issues Jacob mentioned. Also keep in mind that what you're trying to build will look highly suspicious to any security software, it's much easier to just upload the file instead. It would also be a very easy way to base64 encode a PHP shell and upload that too
    – Tom J Nowell
    Commented Jan 25 at 14:05

1 Answer 1

0

The issue with your code is that the testCatalog function is being called on every page load due to its hook into the init action. Since init runs on every WordPress request (both front-end and admin), your function is uploading the image each time a page is loaded. This is why you see the same image being uploaded multiple times.

To resolve this, you need to modify your approach. Here are a few suggestions:

Limit the Function Call: Instead of hooking into init, consider using a different approach to trigger the image upload. For instance, you might hook into a form submission, a specific admin page load, or a custom action that only occurs under certain conditions.

Add a Check to Prevent Duplicate Uploads: You can add logic to check if the image already exists in the media library before uploading it. This can be done by comparing the file name, or by storing and checking a flag in the database once the image is uploaded for the first time.

Here's an example of how you might modify your function to include a check for existing images:

class Catalog
{
    // ... [rest of your class code]

    public function imageExists($hashed_filename) {
        $args = array(
            'post_type' => 'attachment',
            'name' => sanitize_title($hashed_filename),
            'posts_per_page' => 1,
            'post_status' => 'inherit',
        );
        $_header = get_posts($args);
        return count($_header) > 0;
    }

    public function uploadBase64Image()
    {
        // ... [rest of your uploadBase64Image code]

        // Check if the image already exists
        if ($this->imageExists($hashed_filename)) {
            return; // Skip uploading if image exists
        }

        // ... [rest of your uploadBase64Image code]
    }
}

And modify the action hook to a more appropriate trigger:

function testCatalog() {
    if (/* your specific condition */) {
        $catalog = new Catalog();
        return $catalog->uploadBase64Image();
    }
}
// Replace 'init' with a more suitable action hook
// add_action('your_custom_action', 'testCatalog');

In the above code, replace /* your specific condition */ with the actual condition that should trigger the upload. For example, it could be a specific page load, a form submission, or a custom admin action. This will prevent the function from running on every page load and only run when your specific condition is met.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.