Replace a Media File and Keep Its URL
Adds a "Replace file" box to the media edit screen that uploads a new version of an image, PDF or other file over the old one, keeping the same address so every link and embed updates at once.
Opens WPIDE on
Code
add_action( 'add_meta_boxes_attachment', function ( $post ) {
if ( ! current_user_can( 'edit_post', $post->ID ) ) {
return;
}
add_meta_box( 'wpide-replace-file', 'Replace file', function ( $post ) {
wp_nonce_field( 'wpide_replace_' . $post->ID, 'wpide_replace_nonce' );
printf(
'<p><label for="wpide-replace-input">Upload a new %s file to replace this one:</label></p><p><input type="file" id="wpide-replace-input" name="wpide_replace_file" accept="%s"></p><p class="description">Then click Update. The URL stays the same.</p>',
esc_html( strtoupper( pathinfo( (string) get_attached_file( $post->ID ), PATHINFO_EXTENSION ) ) ),
esc_attr( $post->post_mime_type )
);
}, 'attachment', 'side' );
} );
// The attachment form needs multipart encoding for file uploads.
add_action( 'post_edit_form_tag', function ( $post ) {
if ( 'attachment' === $post->post_type ) {
echo ' enctype="multipart/form-data"';
}
} );
add_action( 'edit_attachment', function ( $attachment_id ) {
if ( empty( $_FILES['wpide_replace_file']['tmp_name'] ) || ! isset( $_POST['wpide_replace_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['wpide_replace_nonce'] ), 'wpide_replace_' . $attachment_id ) || ! current_user_can( 'edit_post', $attachment_id ) ) {
return;
}
$upload = $_FILES['wpide_replace_file']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$old = get_attached_file( $attachment_id );
$check = wp_check_filetype_and_ext( $upload['tmp_name'], $upload['name'] );
if ( UPLOAD_ERR_OK !== (int) $upload['error'] || ! $check['type'] || $check['type'] !== get_post_mime_type( $attachment_id ) || ! is_uploaded_file( $upload['tmp_name'] ) ) {
set_transient( 'wpide_replace_error_' . get_current_user_id(), 'The new file must be a valid ' . get_post_mime_type( $attachment_id ) . ' file.', 60 );
return;
}
// Remove the old intermediate sizes before writing the new original.
$meta = wp_get_attachment_metadata( $attachment_id );
if ( ! empty( $meta['sizes'] ) ) {
foreach ( $meta['sizes'] as $size ) {
wp_delete_file( path_join( dirname( $old ), $size['file'] ) );
}
}
if ( ! empty( $meta['original_image'] ) ) {
$old = path_join( dirname( $old ), $meta['original_image'] );
update_attached_file( $attachment_id, $old );
}
if ( move_uploaded_file( $upload['tmp_name'], $old ) ) {
require_once ABSPATH . 'wp-admin/includes/image.php';
wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $old ) );
clean_post_cache( $attachment_id );
}
} );
add_action( 'admin_notices', function () {
$key = 'wpide_replace_error_' . get_current_user_id();
if ( $message = get_transient( $key ) ) {
delete_transient( $key );
printf( '<div class="notice notice-error"><p>%s</p></div>', esc_html( $message ) );
}
} );
Notes
The new file must be the same type (for example JPEG for JPEG); image sizes are regenerated automatically. Browsers and CDNs may show the old file until their cache expires.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.