media_handle_upload()WP 2.5.0

Uploads the file provided in the form to the WordPress uploads folder and creates a record of the file in the database (adds the file to the WP media library).

Works with the global variable $_FILES, the function needs to specify the index of the $_FILES array that contains data about the uploaded file, and the function will upload the file to the WordPress uploads folder and create a record of the attachment in the wp_posts table in the database. In the second parameter, you need to specify the ID of the post to which the uploaded media file should be attached.

To use this function on the front end, you need to include the files:

require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';

When you need to work with arbitrary file data, rather than the bulk $_FILES, use media_handle_sideload().

No Hooks.

Returns

Int|WP_Error. ID of the created attachment. WP_error if the upload failed.

Usage

media_handle_upload( $file_id, $post_id, $post_data, $overrides );
$file_id(string) (required)
The index of the $_FILES array element that contains data about the accepted file (name, type, size, temporary location).
$post_id(number) (required)
The ID of the post to which the media file will be attached. If you do not want the file to be attached to a post, specify 0 in the parameter.
$post_data(array)
Allows you to overwrite the data of the created attachment. Here you specify the data that will be written to the wp_posts table for the created attachment: post_parent, post_title, post_excerpt, etc.
Default: array() (default data)
$overrides(array)
Allows you to change the behavior of the wp_handle_upload() function, on which the current function is based.
Default: array( 'test_form' => false )

Examples

0

#1 Upload and save attachment file using custom HTML form

First, here's an HTML form example with a file upload field. Presumably the code is installed in the front end of the site:

<form id="featured_upload" method="post" action="#" enctype="multipart/form-data">

	<input type="file" name="my_image_upload" id="my_image_upload"  multiple="false" />
	<input type="hidden" name="post_id" id="post_id" value="55" />
	<?php wp_nonce_field( 'my_image_upload', 'my_image_upload_nonce' ); ?>
	<input id="submit_my_image_upload" name="submit_my_image_upload" type="submit" value="Upload" />
</form>

Make sure you use the enctype="multipart/form-data" attribute for the form.
If you don't, $_FILES will be empty and media_handle_upload() will return an error.

Now, handle the form submission and save the attachment file:

// Check the nonce protection and that the user can edit this post.
if (
	isset( $_POST['my_image_upload_nonce'], $_POST['post_id'] )
	&& wp_verify_nonce( $_POST['my_image_upload_nonce'], 'my_image_upload' )
	&& current_user_can( 'edit_post', $_POST['post_id'] )
) {
	// all ok! Moving on.
	// These files must be connected in the front end (front-end).
	require_once ABSPATH . 'wp-admin/includes/image.php';
	require_once ABSPATH . 'wp-admin/includes/file.php';
	require_once ABSPATH . 'wp-admin/includes/media.php';

	// Catch the download using WordPress native function.
	// Don't forget to specify the name attribute of the input field - 'my_image_upload'
	$attachment_id = media_handle_upload( 'my_image_upload', $_POST['post_id'] );

	if ( is_wp_error( $attachment_id ) ) {
		echo 'Error loading media file';
	} else {
		echo 'The media file was successfully uploaded!';
	}

} 
else {
	echo 'Check failed. Unable to download file.';
}
0

#2 Creating multiple file uploads in WordPress

WordPress allows you to upload multiple files at once, even with drag-and-drop capability, in the Admin panel. If you need to add such a feature on the front part of the site, it is not so difficult. To do this, let's create a page template (my-upload-page.php) and place the following form there:

<form action="" method="post" enctype="multipart/form-data" name="front_end_upload" >

	<label> Add all files here: <input type="file" name="kv_multiple_attachments[]" multiple="multiple"> </label>

	<input type="submit" name="Upload" >
</form>

This form sends data to its own page (action=""), so in the code of the same file (my-upload-page.php) of the template you need to place a handler, see the example above.

Changelog

Since 2.5.0 Introduced.

media_handle_upload() code WP 6.8.3

function media_handle_upload( $file_id, $post_id, $post_data = array(), $overrides = array( 'test_form' => false ) ) {
	$time = current_time( 'mysql' );
	$post = get_post( $post_id );

	if ( $post ) {
		// The post date doesn't usually matter for pages, so don't backdate this upload.
		if ( 'page' !== $post->post_type && substr( $post->post_date, 0, 4 ) > 0 ) {
			$time = $post->post_date;
		}
	}

	$file = wp_handle_upload( $_FILES[ $file_id ], $overrides, $time );

	if ( isset( $file['error'] ) ) {
		return new WP_Error( 'upload_error', $file['error'] );
	}

	$name = $_FILES[ $file_id ]['name'];
	$ext  = pathinfo( $name, PATHINFO_EXTENSION );
	$name = wp_basename( $name, ".$ext" );

	$url     = $file['url'];
	$type    = $file['type'];
	$file    = $file['file'];
	$title   = sanitize_text_field( $name );
	$content = '';
	$excerpt = '';

	if ( preg_match( '#^audio#', $type ) ) {
		$meta = wp_read_audio_metadata( $file );

		if ( ! empty( $meta['title'] ) ) {
			$title = $meta['title'];
		}

		if ( ! empty( $title ) ) {

			if ( ! empty( $meta['album'] ) && ! empty( $meta['artist'] ) ) {
				/* translators: 1: Audio track title, 2: Album title, 3: Artist name. */
				$content .= sprintf( __( '"%1$s" from %2$s by %3$s.' ), $title, $meta['album'], $meta['artist'] );
			} elseif ( ! empty( $meta['album'] ) ) {
				/* translators: 1: Audio track title, 2: Album title. */
				$content .= sprintf( __( '"%1$s" from %2$s.' ), $title, $meta['album'] );
			} elseif ( ! empty( $meta['artist'] ) ) {
				/* translators: 1: Audio track title, 2: Artist name. */
				$content .= sprintf( __( '"%1$s" by %2$s.' ), $title, $meta['artist'] );
			} else {
				/* translators: %s: Audio track title. */
				$content .= sprintf( __( '"%s".' ), $title );
			}
		} elseif ( ! empty( $meta['album'] ) ) {

			if ( ! empty( $meta['artist'] ) ) {
				/* translators: 1: Audio album title, 2: Artist name. */
				$content .= sprintf( __( '%1$s by %2$s.' ), $meta['album'], $meta['artist'] );
			} else {
				$content .= $meta['album'] . '.';
			}
		} elseif ( ! empty( $meta['artist'] ) ) {

			$content .= $meta['artist'] . '.';

		}

		if ( ! empty( $meta['year'] ) ) {
			/* translators: Audio file track information. %d: Year of audio track release. */
			$content .= ' ' . sprintf( __( 'Released: %d.' ), $meta['year'] );
		}

		if ( ! empty( $meta['track_number'] ) ) {
			$track_number = explode( '/', $meta['track_number'] );

			if ( is_numeric( $track_number[0] ) ) {
				if ( isset( $track_number[1] ) && is_numeric( $track_number[1] ) ) {
					$content .= ' ' . sprintf(
						/* translators: Audio file track information. 1: Audio track number, 2: Total audio tracks. */
						__( 'Track %1$s of %2$s.' ),
						number_format_i18n( $track_number[0] ),
						number_format_i18n( $track_number[1] )
					);
				} else {
					$content .= ' ' . sprintf(
						/* translators: Audio file track information. %s: Audio track number. */
						__( 'Track %s.' ),
						number_format_i18n( $track_number[0] )
					);
				}
			}
		}

		if ( ! empty( $meta['genre'] ) ) {
			/* translators: Audio file genre information. %s: Audio genre name. */
			$content .= ' ' . sprintf( __( 'Genre: %s.' ), $meta['genre'] );
		}

		// Use image exif/iptc data for title and caption defaults if possible.
	} elseif ( str_starts_with( $type, 'image/' ) ) {
		$image_meta = wp_read_image_metadata( $file );

		if ( $image_meta ) {
			if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) {
				$title = $image_meta['title'];
			}

			if ( trim( $image_meta['caption'] ) ) {
				$excerpt = $image_meta['caption'];
			}
		}
	}

	// Construct the attachment array.
	$attachment = array_merge(
		array(
			'post_mime_type' => $type,
			'guid'           => $url,
			'post_parent'    => $post_id,
			'post_title'     => $title,
			'post_content'   => $content,
			'post_excerpt'   => $excerpt,
		),
		$post_data
	);

	// This should never be set as it would then overwrite an existing attachment.
	unset( $attachment['ID'] );

	// Save the data.
	$attachment_id = wp_insert_attachment( $attachment, $file, $post_id, true );

	if ( ! is_wp_error( $attachment_id ) ) {
		/*
		 * Set a custom header with the attachment_id.
		 * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
		 */
		if ( ! headers_sent() ) {
			header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
		}

		/*
		 * The image sub-sizes are created during wp_generate_attachment_metadata().
		 * This is generally slow and may cause timeouts or out of memory errors.
		 */
		wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
	}

	return $attachment_id;
}