Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove an image that was not put in uploads folder via wp_handle_upload

I'm saving an image to uploads folder, but I'm using file_put_contents instead of wp_handle_upload - because I get an image in base64 and not as a file in $_FILES.

Image and certain post data are saved/updated as they should be using this functions:

  • wp_insert_attachment
  • wp_update_attachment_metadata

The problem is when I want to remove an old image (when saving a new one).

wp_delete_attachment does not remove the image (it does seem to remove stuff in db though..). I am thinking the problem lies in not using wp_handle_upload. (when I upload an image via upload btn and receive it using $_FILES and then upload it with wp_handle_upload - removing works)

Does anyone have an idea of what might be the right way to remove an image in my case? Perhaps I can save it properly using wp_handle_upload even if I have an image in base64?

Thanks for any info.

EDIT: I also tried saving image with wp_upload_bits and wp_delete_attachment still did not work.

Another thing I checked: the code of wp_handle_upload function located in wp-admin/includes/file.php: I don't see an easy way to modify or copy the existing function and add a custom one which would accept base64 image instead of a file as in $_FILES. Someone has a "base64 to $_FILES" workaround perhaps?

like image 769
trainoasis Avatar asked Nov 16 '15 11:11

trainoasis


3 Answers

as @KamilP said, WP inserts records in wp_posts and wp_postmeta tables.

So first you have to save your base64 images in temporary directory, then by using it's relative path and other data you can insert record in database by using wp_insert_attachment, link contains appropriate example. This function will add image to media library.

To generate more thumbnails you can use wp_generate_attachment_metadata function. This function will update wp_postmeta table also with all details of image and thumbnails.

After this you can use wp_delete_attachment function to delete image from directory and database as well.

Another solution:

  • Use function from this link to generate image from base64 string.
  • then get mime type, size and path of image
  • create an array similar to $_FILES's.
  • then pass it to wp_handle_upload

UPDATE :

$_FILES array's structure is like this

array(5) {
    'name'     => string(8) "file name.extension" // file name with extension
    'type'     => string(0) "" // mime type of file, i.e. image/png
    'tmp_name' => string(0) "" // absolute path of file on disk.
    'error'    => int(2) // 0 for no error
    'size'     => int(0) // size in bytes
  }

You can create array like above with all details, use various file handling PHP functions to get size and mime type. Name is whatever you want to put, and tmp_name is a path of file on server where file is exists, in your case location of folder where you save your file from base64 string.

See updated link above for function which will give you image from base64 string.

like image 113
shyammakwana.me Avatar answered Oct 13 '22 17:10

shyammakwana.me


I actually accomplished this before I saw an answer here, so I'm providing a solution here if someone runs into the same problem. I used the function below (first one) and also using wp_delete_attachment after just to be sure all stuff got removed.

/**
 * Attempt at removing all images in uploads folder by providing an image url 
 * Example: 
 *   - Provide http://mypage.com/wp-content/themes/mytheme/uploads/2015/12/testImage.jpg
 *   - this should remove all its images created when uploading:
 *   - testImage-150x150.jpg, testImage-300x300.jpg etc and also the provided original image 
 *
 * We'r doing this because wp_delete_attachment() will not remove an image that was uploaded via a 
 * custom mytheme_upload_image() function (which is providing base64 image instead of file in $_FILES)
 *
 * TODO TODO mytheme_get_image_sizes() does not return ALL IMAGES THAT WERE CREATED (all sizes)
 */
function mytheme_remove_all_image_sizes_from_uploads($primary_image_url) {

    $pi = pathinfo($primary_image_url);

    $img_dirname = $pi['dirname'];
    $img_dirname_exploded = explode('/',$img_dirname);   
    $last_dir = array_pop($img_dirname_exploded); // month usually (two digits)
    $second_last_dir = array_pop($img_dirname_exploded); // year usually (4 digits)

    $basename = $pi['basename']; // without trailing /
    $img_name = $pi['filename'];
    $img_extension = $pi['extension'];

    $uploads = wp_upload_dir();
    $base_uploads_dir = $uploads['basedir']; // without trailing /

    $path_to_appropriate_uploads_dir =      $base_uploads_dir.'/'.$second_last_dir.'/'.$last_dir.'/';

    $img_name_to_remove = $img_name.'.'.$img_extension; // UNLINK

    if(!@unlink($path_to_appropriate_uploads_dir.$img_name_to_remove)) {
       // this image was not removed
    }

    $image_sizes = mytheme_get_image_sizes();

    foreach($image_sizes as $size) {
       $img_name_to_remove = $img_name.'-'.$size.'.'.$img_extension; // UNLINK
       $img_path = $path_to_appropriate_uploads_dir.$img_name_to_remove;

       if(mytheme_image_on_url_exists($img_path) && !@unlink($img_path)) {
          // this image was not removed
       }
    }

 }


    /**
 * Get size information for all currently-registered image sizes.
 * Found an example of this on one of the wordpress' example pages ..
 *
 * @global $_wp_additional_image_sizes
 * @uses   get_intermediate_image_sizes()
 * @return array $sizes Data for all currently-registered image sizes.
 * TODO TODO mytheme_get_image_sizes() does not return ALL IMAGES THAT WERE CREATED (all sizes)
 */
function mytheme_get_image_sizes() {
    global $_wp_additional_image_sizes;

    $sizes = array();

    foreach ( get_intermediate_image_sizes() as $_size ) {

        if ( in_array( $_size, array( 'thumbnail', 'medium', 'large' ) ) ) {

            $width = get_option( "{$_size}_size_w" );
            $height = get_option( "{$_size}_size_h" );

        } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {

            $width = $_wp_additional_image_sizes[ $_size ]['width'];
            $height = $_wp_additional_image_sizes[ $_size ]['height'];

        }

        $img_name_end = $width."x".$height;

        if(!in_array($img_name_end,$sizes)) {
            $sizes[] = $img_name_end;
        }

    }

    // ADD CUSTOM SIZES (this one is not returned otherwise?!)
    $sizes[] = '300x200';

    return $sizes;
}
like image 22
trainoasis Avatar answered Oct 13 '22 15:10

trainoasis


The simple solution is probably to just manually add _wp_attached_file meta value to your attachment.

Set it to the file you want deleted when the attachment is deleted.

The logic behind this is according to https://core.trac.wordpress.org/browser/tags/4.4/src/wp-includes/post.php#L0:

get_attached_file returns that value to wp_delete_attachment which sets it as $file

And at the end of wp_delete_attachment it calls wp_delete_file($file)

So I'm guessing the underlying issue is that, you are never setting a value for the meta key _wp_attached_file.

The only downside is that I'm not sure what other processes in wp might be using that value. But you might get lucky, perhaps none, and/or it doesn't mess anything up by setting it manually.

like image 23
flcoder Avatar answered Oct 13 '22 16:10

flcoder