4.1 (WP 티켓 # 18532)의 이미지 스케일링 (반올림) 변경 관련 문제 처리


17

현재 사이트 내용을 이전 4.1 이전 사이트에서 새 설정으로 마이그레이션하고 반올림 오류 문제 # 18532해당 수정 문제가 발생했습니다 .

이 문제를 요약하면 워드 프레스 측면에서 오래 지속되는 반올림 오작동이 해결되었습니다.

693x173의 이미지를 업로드하고 너비를 300으로 조정한다고 상상해보십시오.

  • 프리 4.1 : 300x74
  • 사후 4.1 : 300x75

문제

일반적으로 기존 파일이 손상되지 않기 때문에 아무런 문제 <img>가 없습니다.

당신이 WXR 파일에서 엄지 손가락 또는 수입 첨부 파일을 재생 그러나 그들은 모두 떠나 파일 시스템에서 다르게 생성 얻을 <img>post_content죽은.

해결책을 찾고

나는 다양한 솔루션을 생각하고있다 :

나쁜 옛날로 돌아가

Changeset 30660 에는 wp_constrain_dimensions4.1 이전의 이전 동작을 다시 연결하는 데 사용할 수 있는 새로운 필터 가 도입 되었습니다. 문제가 해결되었습니다. 그러나 이것이 나중에 문제를 일으킬 수 있는지 궁금해하며 일반적으로 수정을 원하므로 이것이 효과가 있지만 이상적이지 않은 것으로 간주합니다.

그들이 변하는 시간

따라서 이것은 또 다른 목표로 남습니다. DB를 정리하고 이전 파일에 대한 모든 참조를 새 파일에 대한 참조로 바꿉니다. 내가 실제로 여기에서 묻는 질문은 이것을하는 방법입니다. 이 문제가 많은 사람들에게 영향을 줄 것으로 생각되는 효과적이고 일반적으로 적용 가능한 솔루션을 찾고 있습니다.

내 현재 아이디어는 다음과 같습니다.

  1. 가져 오기, 재생성 또는 새 파일 및 깨진 태그가있는 모든 항목
  2. 파일 시스템의 모든 크기 조정 된 파일에서 목록 A를 작성하거나 데이터베이스에서이 정보를 얻습니다.
  3. 이 목록을 구문 분석하고 4.1 이전처럼 파일 이름이 모두 1 픽셀 씩 오프셋 된 두 번째 목록 B를 작성하십시오.
  4. B의 모든 발생을 A의 관련 항목으로 대체하여 전체 데이터베이스를 검색 및 교체하십시오.

이것이이 상황을 처리하는 가장 똑똑하고 효율적인 방법인지 확실하지 않습니다. 또한 너무 무차별 한 느낌이 듭니다. 그래서 그것을 구현하기 전에 WPSE 군중의 무한한 지혜로 확인하고 싶었습니다.)

[편집] ck-macleod 의 답변 을 읽었습니다 (감사합니다!) 나는이 문제를 한 번에 해결해야한다고 생각 하므로이 문제를 계속해서 머리 뒤에 두지 않아도됩니다. [/편집하다]

[edit2] Trac 에서 관련 티켓을 찾았습니다 . 참조를 위해 추가. [/ edit2]


당신은 어디를 error issue of #13852의미 #18532했습니까? :)
Aravona

2
죄송합니다. :)
kraftner

답변:


4

이는 가져 오기 도구를 사용하여 컨텐츠를 가져올 때 작동하고 URL을 한 번에 수정하는 다른 답변 보다 다른 접근 방법 입니다. 다시 : 이것은 전투 테스트를 거치지 않았지만 제가 해결 한 솔루션입니다.

문제를 한 번에 해결하고 모두 작동하면 작동하는 것이 좋습니다. DB에 깨진 물건을 남기지 않고 디스플레이에 수정하면 나중에 깨지는 것에 대해 걱정할 필요가 없습니다.

/*
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'));

좋은 일 +1.
gmazzap

1

예를 들어, 개인이 때때로 WP 스타일을 모방하는 수동으로 이미지 파일의 이름을 바꾸거나 다른 이상한 변형이있을 수있는 가능성을 고려할 때 대규모 사이트의 모든 이미지 파일 (및 링크)에 대해 전 세계적으로 완벽하게 문제를 해결하는 것은 어려울 수 있습니다. 데이터베이스 검색 및 교체 작업에도 복잡성과 위험이 수반됩니다.

깨진 이미지와 깨진 이미지 링크, 대부분의 오류를 다음과 같은 방법으로 처리하고 원하는 최종 결과 또는 합리적인 팩스를 얻을 수 있습니까?

  1. 새로운 "라운드"방법이 아닌 이전 "intval"방법으로 크기가 조정 된 모든 크기 조정 된 이미지의 날짜를 식별하십시오. (다른 종류의 컷오프도 만들 수 있지만 날짜가 가장 쉬운 것 같습니다.)

  2. <= 컷오프 날짜로 게시 된 모든 게시물에 대해로드 / 렌더링 시간에 the_content ()에서 preg_replace를 실행하여 모든 이미지 파일을 문제 패턴 또는 패턴으로 캡처하고 원하는 패턴으로 바꿉니다. 데이터베이스는 변경되지 않은 상태로 유지되지만 대부분의 경우 출력에 오류가 없습니다. 솔루션이 "단일"페이지 게시물 내용과 페이지 및 기타 프로세스를 모두 보관해야하는지 확실하지 않습니다.

이 유형의 솔루션이 도움이된다면 다음 질문은 문제 패턴과 교체가 적절하게 정의 될 수 있는지 여부입니다. 제안 된 솔루션 목록에서 실제로 몇 가지 전형적인 패턴이 분리 될 수 있다고 생각합니다 (아마도 미리보기 이미지 및 기타 이미지를 생성하는 이전 미디어 설정에서 가져온 것입니다).

지정된 디렉토리의 모든 이미지 파일을 특정 날짜까지 전체 이미지를 기본 이미지 또는 이미지 링크로 전체적으로 대체하는 더 간단한 함수를 이미 작성했습니다 (플러그인으로 전환 중임). 전술 한 방법에 따라. 저작권을 과도하게 고지하면서 운영자는 단순히 모든 이미지를 삭제하고 이전 페이지에서 추악한 결과를 생성 할뿐만 아니라 각각에 대해 수천 개의 오류가 발생한다는 사실을 인식하지 못한 사이트를위한 것입니다. 영상.

문제 패턴을 더 구체적으로 좁힐 수 있고 출력을 변경해야하는 인스턴스 인 경우,이를 내 형식에 연결하는 방법을 알 수 있습니다. 쉽게 할 수 있습니다. 반면에,이 방법으로 문제가 해결되지 않으면 시간을 낭비하고 싶지 않습니다.


이것을 가져 주셔서 감사합니다! 단지 몇 가지 생각 : DB에 잘못된 데이터가 있고 그것을 표시하는 원숭이 패치는 매우 깨끗하고 지속 가능한 솔루션이 아니라고 생각합니다. 시간이 지나면 각보기에서 성능이 저하 될 수 있습니다. 또한 다른 방식으로 콘텐츠를 구문 분석하거나 변경하는 다른 플러그인의 경우 예기치 않은 부작용이있을 수 있습니다. 수행 방식에 따라 백엔드에서 이미지가 여전히 손상됩니다. 이 경우 wp_constrain_dimensions가져 오기를 수행하면서 엄지 손가락을 다시 작성하지 말고 질문에서 언급 한 것처럼 스케일링 비아 를 재설정하는 것이 더 깨끗하다고 ​​생각합니다.
kraftner

천만에요 즉, DB의 데이터는 잘못된 데이터가 아니며 새로운 체제에서 더 이상 원하는 데이터가 아닙니다. 성능 저하에 대해서는 이론상 X 날짜 이전의 게시물에만 적용되기 때문에 최소한으로 생각할 수 있습니다. 더 일반적으로 단일 크기에 맞는 최상의 솔루션이 없을 수도 있습니다. 충분한 솔루션은 사이트의 특성, 과거 이미지 처리 응용 프로그램 및 습관, DB 크기, 실제 및 시간 제약 등에 따라 달라질 수 있습니다.
CK MacLeod

당신에게 맞는 솔루션은 하나도 없을 것입니다. 나는 현재 이것을 처리하는 다양한 방법을 모색하고 있습니다. 여러분과 비슷한 온 렌더 방식과이 문제를 한 번에 해결하기 위해 선호하는 임포트 방식입니다. 우리는 이것이 어디로 인도되는지 볼 것입니다. :)
kraftner

1

자, 이것은 고장난 이미지를 즉석에서 교체하는 기본적인 방법입니다. 이것은 테스트를 거친 솔루션보다 개념 증명에 가깝습니다. the_content일부 상황에서 원하지 않는 부작용을 일으킬 수 있는 필터 에 연결하기 만하면 됩니다. 조심히 다루세요. :)

코드에서도 그렇게 말하지만 내 코드에 사용 된 이 답변에 대해 @Rarst 를 신용하고 싶습니다 .

/*
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'));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.