Xử lý các sự cố với thay đổi tỷ lệ hình ảnh (làm tròn) trong 4.1 (Vé WP # 18532)


17

Tôi hiện đang trong quá trình di chuyển nội dung trang web từ một trang web 4.1 cũ sang thiết lập mới và gặp sự cố với vấn đề làm tròn số # 18532 và cách khắc phục tương ứng .

Để tóm tắt điều này đã sửa lỗi một hành vi sai trái làm tròn từ lâu về phía WordPress:

Hãy tưởng tượng chúng tôi tải lên một hình ảnh với 693x173 và chia tỷ lệ thành chiều rộng 300:

  • trước 4,1: 300x74
  • bài 4.1: 300x75

Vấn đề

Nói chung, điều này không gây ra bất kỳ vấn đề nào vì các tệp hiện có và <img>không được chạm vào.

Nhưng khi bạn tái tạo ngón tay cái hoặc file đính kèm nhập khẩu từ một tập tin WXR họ được tạo khác nhau trong hệ thống tập tin để lại tất cả <img>trong post_contentcõi chết.

Tìm kiếm một giải pháp

Tôi đã suy nghĩ về các giải pháp khác nhau:

Trở về thời kỳ tồi tệ

Thay đổi 30660 đã giới thiệu một bộ lọc mới wp_constrain_dimensionscó thể được sử dụng để chỉ cắm hành vi cũ từ trước 4.1 trở lại. Điều này khắc phục sự cố. Nhưng tôi tự hỏi liệu điều này có thể gây ra sự cố sau này hay không và nói chung tôi muốn có cách khắc phục nên mặc dù điều này hoạt động tôi cho là không lý tưởng.

Thời đại họ đang thay đổi

Vì vậy, điều này để lại cho chúng ta một mục tiêu khác: Dọn dẹp DB và thay thế tất cả các tham chiếu đến các tệp cũ bằng các tham chiếu đến các tệp mới. Câu hỏi tôi thực sự hỏi ở đây bây giờ là làm thế nào để làm điều này. Tôi đang tìm kiếm một giải pháp hiệu quả và thường được áp dụng vì tôi nghi ngờ vấn đề này xảy ra và sẽ ảnh hưởng đến rất nhiều người

Ý tưởng hiện tại của tôi là thế này:

  1. Nhập, tạo lại hoặc bất cứ thứ gì để lại cho chúng tôi các tệp mới và các thẻ bị hỏng.
  2. Tạo danh sách A từ tất cả các tệp đã thay đổi kích thước trong hệ thống tệp hoặc thay vào đó nhận thông tin này từ cơ sở dữ liệu
  3. Phân tích danh sách này và tạo danh sách B thứ hai với tên tệp được bù trừ bởi một pixel như nó sẽ nhìn trước 4.1.
  4. Thực hiện tìm kiếm & thay thế toàn bộ cơ sở dữ liệu thay thế tất cả các lần xuất hiện của B bằng mục nhập liên quan trong A

Tôi chỉ không chắc chắn nếu đây là cách thông minh và hiệu quả nhất để xử lý tình huống này. Nó cũng cảm thấy một chút quá sức mạnh. Vì vậy, trước khi thực hiện nó, tôi chỉ muốn kiểm tra trí tuệ vô hạn của đám đông WPSE;)

[sửa] Đã đọc câu trả lời của ck-macleod (cảm ơn!) Tôi nghĩ rằng một bản sửa lỗi sẽ giải quyết vấn đề này một lần và mãi mãi vì vậy bạn không cần phải liên tục giữ vấn đề này ở phía sau đầu. [/biên tập]

[edit2] Tôi vừa tìm thấy một vé liên quan trên Trac . Thêm để tham khảo. [/ edit2]


mà bạn đã error issue of #13852không bạn có nghĩa là #18532? :)
Aravona

2
Rất tiếc, có, đã sửa. :)
kraftner

Câu trả lời:


4

Đây là một cách tiếp cận khác so với câu trả lời khác hoạt động khi nhập nội dung với nhà nhập khẩu và sửa các URL một lần và mãi mãi. Một lần nữa: Đây không phải là thử nghiệm chiến đấu nhưng là giải pháp tôi đã giải quyết và nó đã hoạt động.

Tôi thích điều này vì nó giải quyết vấn đề một lần và mãi mãi và nếu nó hoạt động, nó hoạt động. Vì bạn không để lại những thứ bị hỏng trong DB và sửa nó trên màn hình, bạn không cần phải lo lắng về những thứ bị hỏng sau này.

/*
Plugin Name:  Bugfix Ticket 31581 for WP_Importer
Plugin URI:   /wordpress//a/206992/47733
Description:  Fixes image references after post WP 4.1 image scaling change in post content when using the WP_Importer  (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581WPImporter {

    protected $remaps;

    /**
     * Initialize class, mainly setting up hooks
     */
    public function init(){

        if (WP_IMPORTING === true){

            $this->remaps = array();

            //This hook is chosen because it is pretty close to where the actual attachment import is happening.
            //TODO: May be reconsidered.
            add_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10 , 2);

            add_action('import_end', array($this, 'remap'));
            add_action('import_end', array($this, 'importEnded'));
        }

    }

    /**
     * Cleans up hooks after the import has ended.
     */
    public function importEnded(){
        remove_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10);

        remove_action('import_end', array($this, 'remap'), 10);
        remove_action('import_end', array($this, 'importEnded'), 10);
    }

    /**
     * When an attachment is added compare the resulting sizes with the sizes from the legacy algorithm and setup remap.
     *
     * @param $data
     * @param $post_id
     *
     * @return mixed
     */
    public function collectRemaps($data, $post_id ){

        $intermediate_sizes = $this->getIntermediateSizes();

        if(empty($data) || !array_key_exists('sizes', $data)){
            return $data;
        }

        foreach($data['sizes'] as $key => $size){

            $size_new_algorithm = array($size['width'], $size['height']);

            $dest_w = $intermediate_sizes[$key]['width'];
            $dest_h = $intermediate_sizes[$key]['height'];
            $crop = $intermediate_sizes[$key]['crop'];

            add_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10, 5);

            $size_old_algorithm = image_resize_dimensions($data['width'], $data['height'], $dest_w, $dest_h, $crop);

            //Bail out in the rare case of `image_resize_dimensions` returning false
            if($size_old_algorithm===false){
                continue;
            }

            $size_old_algorithm = array($size_old_algorithm[4], $size_old_algorithm[5]);

            remove_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10);

            // Compare the current size with the calculation of the old algorithm...
            $diff = array_diff($size_new_algorithm, $size_old_algorithm);

            // ...to detect any mismatches
            if(!empty($diff)){

                $oldFilename = $this->getOldFilename($size['file'], $size_old_algorithm);

                // If getting the old filename didn't work for some reason (probably other filename-structure) bail out.
                if($oldFilename===false){
                    continue;
                }

                if(!array_key_exists($post_id, $this->remaps)){
                    $this->remaps[$post_id] = array();
                }

                $this->remaps[$post_id][$size['file']] = array(
                    'file' => $oldFilename,
                    'size' => $key
                );
            }

        }

        return $data;
    }

    /**
     * Get resize settings for all image sizes
     *
     * Taken from wp_generate_attachment_metadata() in includes/image.php
     *
     * @return array
     */
    public function getIntermediateSizes(){

        global $_wp_additional_image_sizes;

        $sizes = array();
        foreach ( get_intermediate_image_sizes() as $s ) {
            $sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
            if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
                $sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
            else
                $sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
                $sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
            else
                $sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
                $sizes[$s]['crop'] = $_wp_additional_image_sizes[$s]['crop']; // For theme-added sizes
            else
                $sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
        }

        return $sizes;
    }

    /**
     * Turn the new filename into the old filename reducing the height by one
     *
     * @param $newFilename
     * @param $size
     *
     * @return mixed
     */
    public function getOldFilename($newFilename, $size){

        $dimensions = array();

        $filetypes = $this->getAllowedImageExtentions();

        // TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation.
        $matchFiles = '/([0-9]{1,5})x([0-9]{1,5}).(' . $filetypes . ')$/';

        // Extract the dimensions
        preg_match($matchFiles,$newFilename,$dimensions);

        // If the file URL doesn't allow guessing the dimensions bail out.
        if(empty($dimensions)){
            return $newFilename;
        }

        $newStub = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

        $oldStub = $size[0] . 'x' . $size[1] . '.' . $dimensions[3];

        $oldFilename = str_replace($newStub,$oldStub,$newFilename);

        return $oldFilename;
    }

    /**
     * Extract all file extensions that match an image/* mime type
     *
     * @return string
     */
    protected function getAllowedImageExtentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }


    /**
     * This is the heart of this class. Based on the collected remaps from earlier it does a S&R on the DB.
     */
    public function remap(){

        global $wpdb;

        foreach($this->remaps as $attachment_id => $replaces){

            foreach($replaces as $new_url => $old_data){

                $to_url = wp_get_attachment_image_src($attachment_id,$old_data['size']);
                $to_url = $to_url[0];

                $from_url = str_replace($new_url, $old_data['file'], $to_url);

                // remap urls in post_content
                $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );

                //TODO: This is disabled as enclosures can't be images, right?
                // remap enclosure urls
                //$result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );

            }

        }

    }

    /**
     * This is a copy of the legacy pre 4.1 wp_constrain_dimensions()
     *
     * @param $dimensions
     * @param $current_width
     * @param $current_height
     * @param $max_width
     * @param $max_height
     *
     * @return array
     */
    public function legacy_wp_constrain_dimensions($dimensions, $current_width, $current_height, $max_width, $max_height){
        if ( !$max_width and !$max_height )
            return array( $current_width, $current_height );

        $width_ratio = $height_ratio = 1.0;
        $did_width = $did_height = false;

        if ( $max_width > 0 && $current_width > 0 && $current_width > $max_width ) {
            $width_ratio = $max_width / $current_width;
            $did_width = true;
        }

        if ( $max_height > 0 && $current_height > 0 && $current_height > $max_height ) {
            $height_ratio = $max_height / $current_height;
            $did_height = true;
        }

        // Calculate the larger/smaller ratios
        $smaller_ratio = min( $width_ratio, $height_ratio );
        $larger_ratio  = max( $width_ratio, $height_ratio );

        if ( intval( $current_width * $larger_ratio ) > $max_width || intval( $current_height * $larger_ratio ) > $max_height )
            // The larger ratio is too big. It would result in an overflow.
            $ratio = $smaller_ratio;
        else
            // The larger ratio fits, and is likely to be a more "snug" fit.
            $ratio = $larger_ratio;

        // Very small dimensions may result in 0, 1 should be the minimum.
        $w = max ( 1, intval( $current_width  * $ratio ) );
        $h = max ( 1, intval( $current_height * $ratio ) );

        // Sometimes, due to rounding, we'll end up with a result like this: 465x700 in a 177x177 box is 117x176... a pixel short
        // We also have issues with recursive calls resulting in an ever-changing result. Constraining to the result of a constraint should yield the original result.
        // Thus we look for dimensions that are one pixel shy of the max value and bump them up
        if ( $did_width && $w == $max_width - 1 )
            $w = $max_width; // Round it up
        if ( $did_height && $h == $max_height - 1 )
            $h = $max_height; // Round it up

        return array( $w, $h );
    }

}

add_filter('import_start',array(new Bugfix31581WPImporter(),'init'));

Làm việc tốt +1.
gmazzap

1

Việc giải quyết vấn đề toàn cầu và hoàn hảo cho TẤT CẢ các tệp hình ảnh (và liên kết) trong một trang web lớn - chẳng hạn, có khả năng, các cá nhân đôi khi có thể đổi tên các tệp hình ảnh theo cách thủ công theo kiểu WP - và các biến thể kỳ lạ khác - có thể khó khăn. Các hoạt động tìm kiếm và thay thế cơ sở dữ liệu cũng sẽ liên quan đến các biến chứng (và rủi ro!).

Bạn có thể xử lý phần lớn lỗi - hình ảnh bị hỏng và liên kết hình ảnh bị hỏng, tôi đoán - và đạt được kết quả cuối cùng mong muốn hoặc fax hợp lý, bằng phương pháp sau?

  1. Xác định ngày trước đó tất cả các hình ảnh đã thay đổi kích thước trong đó thay đổi kích thước theo phương pháp "intval" cũ thay vì phương pháp "làm tròn" mới. (Một loại cắt khác cũng có thể được tạo ra, nhưng ngày có vẻ dễ nhất.)

  2. Đối với tất cả các bài đăng được xuất bản <= ngày hết hạn, hãy chạy preg numplace trên the_content () tại thời điểm tải / kết xuất, chụp tất cả các tệp hình ảnh bằng mẫu hoặc mẫu vấn đề và thay thế chúng bằng mẫu mong muốn. Cơ sở dữ liệu sẽ vẫn không thay đổi, nhưng trong hầu hết các trường hợp, đầu ra sẽ không có lỗi. Tôi không chắc liệu giải pháp có cần áp dụng cả cho nội dung bài đăng "số ít" hay không và lưu trữ các trang cũng như các quy trình khác.

Nếu một giải pháp loại này sẽ hữu ích, thì câu hỏi tiếp theo sẽ là liệu các mẫu vấn đề và sự thay thế có thể được xác định đầy đủ hay không. Nó nhìn từ danh sách các giải pháp được đề xuất của bạn mà trên thực tế có thể một vài mẫu điển hình có thể bị cô lập (có lẽ được lấy từ các cài đặt phương tiện trước đó tạo ra hình thu nhỏ và một số hình ảnh khác).

Tôi đã viết một hàm đơn giản hơn mà tôi sử dụng (và đang trong quá trình biến thành plugin), trên toàn cầu thay thế tất cả các tệp hình ảnh trong các thư mục được chỉ định, cho đến một ngày nhất định, với một hình ảnh hoặc liên kết hình ảnh mặc định, theo phương pháp được mô tả ở trên. Đó là một trang web trong đó, vượt quá sự thận trọng về bản quyền, các nhà khai thác chỉ cần xóa tất cả các hình ảnh của họ, mà không biết rằng, ngoài việc tạo ra kết quả xấu trên các trang cũ, họ còn phát hiện ra hàng ngàn lỗi, mỗi lỗi cho mỗi trang hình ảnh.

Nếu bạn có thể thu hẹp mô hình vấn đề cụ thể hơn và các trường hợp cần thay đổi đầu ra, thì tôi có thể thấy về việc cắm nó vào định dạng của mình - điều này không phức tạp lắm và đối với RegExer tốt hơn tôi có thể thậm chí là dễ dàng Mặt khác, tôi sẽ không muốn lãng phí thời gian của bạn hoặc tôi nếu phương pháp này không trả lời được vấn đề cho bạn.


Cảm ơn bạn đã đưa về điều này! Chỉ là một vài suy nghĩ: Tôi nghĩ rằng có dữ liệu sai trong DB và chỉ cần vá nó trên màn hình không phải là một giải pháp rất sạch sẽ và bền vững. Nó có thể phá vỡ bất kỳ lúc nào và gây hại cho hiệu suất trên mỗi chế độ xem. Nó cũng có thể có tác dụng phụ không thể đoán trước, ví dụ như đối với các plugin khác phân tích hoặc thay đổi nội dung theo một cách khác. Tùy thuộc vào cách nó được thực hiện, hình ảnh vẫn bị hỏng trong phần phụ trợ. Trong trường hợp đó, tôi nghĩ chỉ cần đặt lại tỷ lệ thông qua wp_constrain_dimensionsnhư được đề cập trong câu hỏi trong khi thực hiện nhập và hạn chế xây dựng lại ngón tay cái sẽ sạch hơn.
kraftner

Bạn khá hoan nghênh. Điều đáng nói là, dữ liệu trong DB không phải là dữ liệu sai, đó không phải là dữ liệu bạn muốn nữa theo chế độ mới. Về hiệu suất, tôi nghĩ rằng nó có thể là tối thiểu, vì lý thuyết chỉ áp dụng cho các bài đăng trước ngày X. Nói chung, có thể không có giải pháp tốt nhất phù hợp với một kích thước: Tôi nghĩ rằng tốt giải pháp đủ có thể thay đổi theo đặc điểm của trang web, các ứng dụng và thói quen xử lý hình ảnh trong quá khứ, kích thước của DB, các hạn chế thực tế và thời gian, v.v.
CK MacLeod

Bạn có thể đúng rằng sẽ không có giải pháp một kích cỡ phù hợp cho tất cả. Tôi hiện đang khám phá nhiều cách xử lý khác nhau trong số đó là cách tiếp cận kết xuất tương tự như cách tiếp cận của bạn và cách tiếp cận nhập khẩu mà tôi thích vì nó giải quyết điều này một lần và mãi mãi. Chúng ta sẽ thấy điều này dẫn đến đâu. :)
kraftner

1

Được rồi, đây là một cách tiếp cận cơ bản để thay thế hình ảnh bị hỏng khi đang bay. Xin lưu ý rằng đây là một bằng chứng về khái niệm hơn là một giải pháp được thử nghiệm trong trận chiến. Nó chỉ móc vàothe_content bộ lọc có thể (có thể có) một số tác dụng phụ không mong muốn trong một số trường hợp. Xử lý cẩn thận. :)

Mặc dù nó cũng nói như vậy trong mã nhưng tôi cũng muốn ghi có @Rarst cho câu trả lời này được sử dụng trong mã của tôi.

/*
Plugin Name:  Bugfix 31581 Live
Plugin URI:   /wordpress//a/206986/47733
Description:  Fixes image references in post content after post 4.1 image scaling change (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581Live {

    protected $matchURLs;
    protected $matchFiles;

    protected $remaps;

    public function init(){

        $filetypes = $this->get_allowed_image_extentions();

        $baseurl = wp_upload_dir();
        $baseurl = preg_quote($baseurl['baseurl'], '/');

        $this->matchURLs = '/' . $baseurl . '\/.??([a-zA-Z0-9_-]*?\.(?:' . $filetypes . '))/';

        //TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation
        $this->matchFiles = '/([0-9]{1,4})x([0-9]{1,4}).(' . $filetypes . ')$/';

        add_filter('the_content', array($this, 'update_urls') );
    }

    public function update_urls($content){

        $urls = array();

        preg_match_all($this->matchURLs,$content,$urls);

        // Bail out early if we don't have any match.
        if($urls === false || empty($urls[0])){
            return $content;
        }

        // Loop through all matches
        foreach($urls[0] as $url){

            // Try to resolve this URL to an attachment ID
            $id = $this->get_attachment_id($url);

            // If not  let's see if this might be a URL that has been broken by our beloved Changeset 30660
            if( $id === false ){

                $dimensions = array();

                // Extract the dimensions
                preg_match($this->matchFiles,$url,$dimensions);

                // If the file URL doesn't allow guessing the dimensions bail out.
                if(empty($dimensions)){
                    continue;
                }

                // Old filename
                $old = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

                // New filename (not sure if this exists yet)
                $new = $dimensions[1] . 'x' . ($dimensions[2]+1) . '.' . $dimensions[3];

                // Build the new URL (not sure if this exists yet)
                $new_url = str_replace($old,$new,$url);

                // Try to get the attachment with the new url
                $id = $this->get_attachment_id($new_url);

                // Bad luck. This also doesn't exist.
                if( $id === false ) {
                    continue;
                }

                // Just to be sure everything is in sync we get the URL built from id and size.
                $db_url = wp_get_attachment_image_src($id,array($dimensions[1], $dimensions[2]+1));

                // Check if the URL we created and the one wp_get_attachment_image_src builds are the same.
                if($new_url === $db_url[0]){

                    // Awesome let's replace the broken URL.
                    $content = str_replace($url,$new_url,$content);
                }

            }

        }

        return $content;
    }

    /**
     * Get the Attachment ID for a given image URL.
     *
     * @link   /wordpress//a/7094
     *
     * @param  string $url
     *
     * @return boolean|integer
     */
    protected function get_attachment_id( $url ) {

        $dir = wp_upload_dir();

        // baseurl never has a trailing slash
        if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
            // URL points to a place outside of upload directory
            return false;
        }

        $file  = basename( $url );
        $query = array(
            'post_type'  => 'attachment',
            'fields'     => 'ids',
            'meta_query' => array(
                array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                ),
            )
        );

        $query['meta_query'][0]['key'] = '_wp_attached_file';

        // query attachments
        $ids = get_posts( $query );

        if ( ! empty( $ids ) ) {

            foreach ( $ids as $id ) {

                $tmp = wp_get_attachment_image_src( $id, 'full' );

                // first entry of returned array is the URL
                if ( $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        $query['meta_query'][0]['key'] = '_wp_attachment_metadata';

        // query attachments again
        $ids = get_posts( $query );

        if ( empty( $ids) )
            return false;

        foreach ( $ids as $id ) {

            $meta = wp_get_attachment_metadata( $id );

            foreach ( $meta['sizes'] as $size => $values ) {

                $tmp = wp_get_attachment_image_src( $id, $size );

                if ( $values['file'] === $file && $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        return false;
    }

    protected function get_allowed_image_extentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }

}

add_filter('init',array(new Bugfix31581Live(),'init'));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.