I am using wp_upload_bits() to copy an obtained remote image…

$upload = wp_upload_bits($file, '', wp_remote_retrieve_body( $get ));

Image source example: https://logo.clearbit.com/starcomww.com

The following code seems to obtain the image well enough:

$get = wp_remote_get( $image_url ); 
$type = wp_remote_retrieve_header( $get, 'content-type' );

$file (name of file) is set at eg. starcom-mediavest-group.png

So I see how wp_upload_bits($file… would set the filename.

But I don’t see how to add the image to a subdirectory of uploads – specifically called uploads/org_logos
then wp_upload_bits will strip out the slash, believing it to be a non-safe file character, leading to the incorrect file org_logosstarcom-mediavest-group.png being placed in the main uploads folder. It really wants to put it in uploads!

How do I get the image in a subfolder?

I’m aware of the PHP function move_uploaded_file – but I’m concerned that this would lose any references for a file I’ll still need to attach to the Media Library. Regardless, I’m not having much luck moving it with this anyway…

// Move it
$thefile = $upload['file'];
$upload_dir = wp_upload_dir();
$newfolder = $upload_dir['basedir'].'/org_logos';
move_uploaded_file($thefile, $newfolder);

1
1

wp_upload_bits() uses wp_upload_dir() which fires the upload_dir filter allowing you to modify the upload directory’s path, sub-directory and URL.

So you can use that filter to “force” wp_upload_bits() to use a custom path by doing something like:

// Demo to get the image data.
$url="https://logo.clearbit.com/starcomww.com";
$content = file_get_contents( $url );

$_filter = true; // For the anonymous filter callback below.
add_filter( 'upload_dir', function( $arr ) use( &$_filter ){
    if ( $_filter ) {
        $folder="/org_logos"; // No trailing slash at the end.
        $arr['path'] .= $folder;
        $arr['url'] .= $folder;
        $arr['subdir'] .= $folder;
    }

    return $arr;
} );

$filename = wp_basename( $url ) . '.png';
$ret = wp_upload_bits( $filename, null, $content );
var_dump( $ret ); // For testing.

$_filter = false; // Disables the filter.

Or you can use a named callback:

function my_org_logos_upload_dir( $arr ) {
    $folder="/org_logos"; // No trailing slash at the end.

    $arr['path'] .= $folder;
    $arr['url'] .= $folder;
    $arr['subdir'] .= $folder;

    return $arr;
}

// Demo to get the image data.
$url="https://logo.clearbit.com/starcomww.com";
$content = file_get_contents( $url );

add_filter( 'upload_dir', 'my_org_logos_upload_dir' );

$filename = wp_basename( $url ) . '.png';
$ret = wp_upload_bits( $filename, null, $content );
var_dump( $ret ); // For testing.

remove_filter( 'upload_dir', 'my_org_logos_upload_dir' );

UPDATE

(In reply to your comments)

So, as a filter on wp_upload_dir(), that would apply to any file
getting uploaded via wp_upload_dir(), is that right?

Yes, it would.

But the issue can be prevented by adding/enabling the filter only when you call wp_upload_bits() and that you want it to use your custom upload path, and then remove/disable the filter after it’s applied — to prevent the upload path from being changed when wp_upload_dir() is called from other functions or code.

And for that reason, these are my approaches as you have seen in the original answer:

// First variant of the code - using an anonymous function.
$_filter = true;  // enables the filter
$_filter = false; // disables the filter

// Second variant of the code - using a named function.
remove_filter( 'upload_dir', 'my_org_logos_upload_dir' ); // enables the filter
add_filter( 'upload_dir', 'my_org_logos_upload_dir' );    // disables the filter

should the second property of wp_insert_attachment() be $filename,
$content or some property of $ret[here]?

The second parameter of the wp_insert_attachment() function is the full absolute path to the file that’s to be attached to the post.

And on success, wp_upload_bits() returns an array with file being one of the keys, where the value is the full absolute path of the file that was saved by wp_upload_bits(). (You can check the function’s reference or this note for the other keys.)

So the answer is $ret['file']:

wp_insert_attachment( array(...), $ret['file'] );

Leave a Reply

Your email address will not be published. Required fields are marked *