2

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);
3

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'] );
| improve this answer | |
  • Couple of things: 1. Thanks - this could be really valuable also for other people who I see have posted similar questions. 2. So, as a filter on wp_upload_dir, that would apply to any file getting uploaded via wp_upload_dir, is that right? So it could potentially force all uploads to take the same directory? My site may have other uploads destined for other directories. 3. I haven't tried this yet as I reverted to file_put_contents, but I assume it works. – Robert Andrews Jan 26 '19 at 21:03
  • 1. You're welcome. 2. No, because if you look at the code, the filter is removed soon after the wp_upload_bits() is called. The only difference is the second code/version uses a named/non-anonymous callback - my_org_logos_upload_dir(). 3. Try it out. It wouldn't take a long time.. :) – Sally CJ Jan 26 '19 at 21:13
  • 2
    I've updated the answer and hopefully it's more helpful now. – Sally CJ Jan 27 '19 at 19:23
  • 1
    I uploaded files in a loop and anonymous function created a lot of sub-folders. Usage of named function with add/remove filter resolved that issue. Also, I don't know why but I had to add a trailing slash BEFORE $folder. Without it I was creating duplicated upload folders like 'uploadsorg_logos'. Anyway thank you @SallyCJ, your answer was really helpful for me. – Sharpey Nov 7 '19 at 2:53
  • 1
    Thanks @Sharpey, you're right about the /. :) – Sally CJ Nov 7 '19 at 4:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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