File Upload Handling API
WordPress provides comprehensive APIs for handling file uploads, managing the filesystem, and processing media files in the admin.
Core Upload Functions
wp_handle_upload()
Handles an upload and moves the file to the uploads directory.
$file = wp_handle_upload(
array $_FILES['field_name'],
array $overrides = array( 'test_form' => true ),
string $time = null
);
Returns:
// Success
array(
'file' => '/path/to/uploaded/file.jpg',
'url' => 'https://example.com/wp-content/uploads/2024/01/file.jpg',
'type' => 'image/jpeg',
)
// Error
array(
'error' => 'Error message here',
)
Overrides:
| Key | Type | Description |
|---|---|---|
test_form |
bool | Check if form action matches (default true) |
test_size |
bool | Check upload size limits |
test_type |
bool | Check file type |
mimes |
array | Allowed MIME types |
unique_filename_callback |
callable | Custom filename generator |
media_handle_upload()
Complete upload handler: uploads file + creates attachment post.
$attachment_id = media_handle_upload(
string $file_id, // Key in $_FILES
int $post_id, // Parent post ID (0 for unattached)
array $post_data = array(),
array $overrides = array( 'test_form' => false )
);
Example:
if ( ! empty( $_FILES['my_file']['name'] ) ) {
// Required for media_handle_upload
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
$attachment_id = media_handle_upload( 'my_file', 0 );
if ( is_wp_error( $attachment_id ) ) {
// Handle error
echo $attachment_id->get_error_message();
} else {
// Success
$url = wp_get_attachment_url( $attachment_id );
}
}
media_handle_sideload()
Download a file from URL and add to media library.
$attachment_id = media_handle_sideload(
array $file_array, // Like $_FILES entry
int $post_id,
string $desc = null,
array $post_data = array()
);
Example: Sideload from URL
function sideload_image_from_url( $url, $post_id = 0 ) {
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
// Download to temp location
$tmp = download_url( $url );
if ( is_wp_error( $tmp ) ) {
return $tmp;
}
// Build file array
$file_array = array(
'name' => basename( parse_url( $url, PHP_URL_PATH ) ),
'tmp_name' => $tmp,
);
// Sideload the file
$attachment_id = media_handle_sideload( $file_array, $post_id );
// Clean up temp file on error
if ( is_wp_error( $attachment_id ) ) {
@unlink( $file_array['tmp_name'] );
}
return $attachment_id;
}
download_url()
Downloads a URL to a local temp file.
$tmp_file = download_url(
string $url,
int $timeout = 300,
bool $signature_verification = false
);
Custom Upload Form
HTML Form
function my_upload_form() {
?>
<form method="post" enctype="multipart/form-data">
<?php wp_nonce_field( 'my_file_upload', 'my_upload_nonce' ); ?>
<p>
<label for="my_file"><?php _e( 'Select File:' ); ?></label>
<input type="file" name="my_file" id="my_file">
</p>
<?php submit_button( __( 'Upload' ) ); ?>
</form>
<?php
}
Processing Upload
add_action( 'admin_init', 'process_my_upload' );
function process_my_upload() {
if ( ! isset( $_POST['my_upload_nonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_POST['my_upload_nonce'], 'my_file_upload' ) ) {
wp_die( 'Security check failed' );
}
if ( ! current_user_can( 'upload_files' ) ) {
wp_die( 'Permission denied' );
}
if ( empty( $_FILES['my_file']['name'] ) ) {
return;
}
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
$attachment_id = media_handle_upload( 'my_file', 0 );
if ( is_wp_error( $attachment_id ) ) {
add_settings_error(
'my_upload',
'upload_error',
$attachment_id->get_error_message(),
'error'
);
} else {
add_settings_error(
'my_upload',
'upload_success',
__( 'File uploaded successfully.' ),
'success'
);
}
}
File Type Validation
Allowed MIME Types
// Get allowed MIME types
$mimes = get_allowed_mime_types();
// Check specific file
$filetype = wp_check_filetype( 'file.pdf', $mimes );
// Returns: array( 'ext' => 'pdf', 'type' => 'application/pdf' )
// Check with full path
$filetype = wp_check_filetype_and_ext( $file, $filename, $mimes );
Add Custom MIME Types
add_filter( 'upload_mimes', 'add_custom_mimes' );
function add_custom_mimes( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
$mimes['webp'] = 'image/webp';
$mimes['json'] = 'application/json';
return $mimes;
}
Restrict File Types
add_filter( 'upload_mimes', 'restrict_upload_mimes' );
function restrict_upload_mimes( $mimes ) {
// Only allow images
return array(
'jpg|jpeg|jpe' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
);
}
WP_Filesystem API
For file operations that might require FTP credentials.
Initialize Filesystem
function init_filesystem() {
global $wp_filesystem;
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
// Request credentials if needed
$creds = request_filesystem_credentials( '', '', false, false, null );
if ( ! $creds ) {
return false;
}
// Initialize
if ( ! WP_Filesystem( $creds ) ) {
request_filesystem_credentials( '', '', true, false, null );
return false;
}
return true;
}
Filesystem Operations
global $wp_filesystem;
// Read file
$content = $wp_filesystem->get_contents( $file_path );
// Write file
$wp_filesystem->put_contents( $file_path, $content, FS_CHMOD_FILE );
// Check if file exists
if ( $wp_filesystem->exists( $file_path ) ) {
// ...
}
// Create directory
$wp_filesystem->mkdir( $dir_path );
// Delete file
$wp_filesystem->delete( $file_path );
// Delete directory (recursive)
$wp_filesystem->delete( $dir_path, true );
// Copy file
$wp_filesystem->copy( $source, $destination );
// Move file
$wp_filesystem->move( $source, $destination );
// Get directory listing
$files = $wp_filesystem->dirlist( $dir_path );
Upload Directory Functions
// Get upload directory info
$upload_dir = wp_upload_dir();
/*
array(
'path' => '/var/www/wp-content/uploads/2024/01',
'url' => 'https://example.com/wp-content/uploads/2024/01',
'subdir' => '/2024/01',
'basedir' => '/var/www/wp-content/uploads',
'baseurl' => 'https://example.com/wp-content/uploads',
'error' => false,
)
*/
// Get for specific date
$upload_dir = wp_upload_dir( '2023/06' );
Media Library Integration
Using wp.media JavaScript
add_action( 'admin_enqueue_scripts', 'enqueue_media_uploader' );
function enqueue_media_uploader( $hook ) {
if ( 'toplevel_page_my-plugin' !== $hook ) {
return;
}
wp_enqueue_media();
wp_enqueue_script(
'my-media-uploader',
plugin_dir_url( __FILE__ ) . 'js/uploader.js',
array( 'jquery' ),
'1.0.0',
true
);
}
JavaScript (uploader.js):
jQuery(document).ready(function($) {
var frame;
$('#upload_button').on('click', function(e) {
e.preventDefault();
if (frame) {
frame.open();
return;
}
frame = wp.media({
title: 'Select or Upload Image',
button: {
text: 'Use this image'
},
multiple: false,
library: {
type: 'image'
}
});
frame.on('select', function() {
var attachment = frame.state().get('selection').first().toJSON();
$('#image_id').val(attachment.id);
$('#image_preview').attr('src', attachment.url);
});
frame.open();
});
$('#remove_button').on('click', function(e) {
e.preventDefault();
$('#image_id').val('');
$('#image_preview').attr('src', '');
});
});
Import Uploads
wp_import_handle_upload()
For importer file uploads.
$result = wp_import_handle_upload();
if ( isset( $result['error'] ) ) {
echo $result['error'];
} else {
$file = $result['file'];
$id = $result['id'];
// Process import file...
// Clean up when done
wp_import_cleanup( $id );
}
Hooks
| Hook | Description |
|---|---|
upload_mimes |
Filter allowed MIME types |
wp_handle_upload_prefilter |
Filter file data before upload |
wp_handle_upload |
After upload, before moving |
wp_upload_bits |
After wp_upload_bits() |
upload_dir |
Filter upload directory paths |
pre_move_uploaded_file |
Before moving uploaded file |
Best Practices
- Verify Nonces: Always use nonce verification for uploads
- Check Capabilities: Verify user can upload files
- Validate File Types: Use MIME type checking
- Sanitize Filenames: WordPress does this, but validate extensions
- Handle Errors: Check for WP_Error returns
- Clean Up: Delete temp files on failure
- Use Overrides: Set appropriate overrides for context
function secure_file_upload( $file_input ) {
// Verify nonce
check_admin_referer( 'my_upload_action' );
// Check capability
if ( ! current_user_can( 'upload_files' ) ) {
return new WP_Error( 'no_permission', 'You cannot upload files.' );
}
// Validate file presence
if ( empty( $_FILES[ $file_input ]['name'] ) ) {
return new WP_Error( 'no_file', 'No file was uploaded.' );
}
// Custom MIME type restriction
$allowed = array( 'image/jpeg', 'image/png', 'image/gif' );
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
$overrides = array(
'test_form' => false,
'mimes' => array(
'jpg|jpeg|jpe' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
),
);
return media_handle_upload( $file_input, 0, array(), $overrides );
}