5

数年前、私は PHP (ZEND) モジュールを作成し、現在でもプロジェクトの一部で使用しています。このモジュールは、PHP 画像操作のかなり初歩的な (つまり、コピーパスタ) 理解を基に作成されましたが、1 つのケースを除いてはうまく動作します。

このモジュールは、テーブルから blob データを取得し、それを画像に解析し、imagcopyresampled() を使用してサイズを変更し、結果の .jpg をブラウザーに送信します。これは、標準のコントローラー アクションとして呼び出されます。

元の画像が Facebook からユーザーによって保存された場合 (つまり、Facebook の画像ビューアーを右クリックし、デスクトップにダウンロードしてからクライアント サイトにアップロードする) を除いて、すべての場合で機能するようです。私はこれを自分で何度もテストし、再現することができました。問題が発生することなく、フォトショップで再保存したときに同じ画像をアップロードすることもできました。

Facebook の画像表示により、ファイル内にある種の追加のメタデータが追加され、システムが壊れていると思われます。

これに対する解決策はありますか?

PHP イメージ モジュールのコードは次のとおりです。

private function _buildImage($mode) {
    //Prepare the output image
    //Currently CROP and RESIZE are using different output onstruction calls
    //So $finalImage is initialized prior to entering the mode blocks.

    $finalImage = imagecreatetruecolor($this->_width, $this->_height);
    $backgroundFillColor = imagecolorallocate($finalImage, RED, BLUE, GREEN);

    imageFill($finalImage, 0, 0, $backgroundFillColor);

    $this->_table = $this->_getTable($mode);

    $image = $this->_request->image;
    $this->_imageData = $this->_table->fetchEntryAsRow($image);

    //Set top and left to 0 to capture the top/left corner of the orignal image.
    $top = 0;
    $left = 0;


    $inputImage = imagecreatefromstring(    $this->_imageData->image);
    list($inputWidth, $inputHeight) = $this->_getImageSize($this->_imageData->image);   

    //Ratio is the target ratio of $this->_width divided by $this->_height, as set in actions.
    //For index thumbnails this ratio is .7
    //For index large images this ratio is 2
    $ratio = $this->_width / $this->_height;


    //define offset width and offset height as being equal to input image width and height
    $offsetWidth = $inputWidth;
    $offsetHeight = $inputHeight;

    //Define Original Ratio as found in the image in the table.
    $inputRatio = $inputWidth / $inputHeight;

    //Rendering maths for RESIZE and CROP modes.
    //RESIZE forces the whole input image to appear within the frame of the output image.
    //CROP forces the output image to contain only the relevantly sized piece of the input image, measured from the middle.

    if($this->_mode == CROP) {
        if($inputRatio > $ratio) {
            //Original image is too wide, use $height as is. Modify $width;
            //define $scale: input is scaled to output along height.
            $scale = $inputHeight / $this->_height;
            //Calculate $left: an integer calculated based on 1/2 of the input width * half of the difference in the rations.
            $left = round(($inputWidth/2)*(($inputRatio-$ratio)/2), 0);
            $inputWidth = round(($inputWidth - ($left*2)), 0);
            $offset = $offsetWidth - $inputWidth;
        } else {
            //Original image is too high, use $width as is.  Modify $height;
            $scale = $inputWidth / $this->_width;
            $inputHeight = round(($this->_height * $scale),0);
            $offset = $offsetHeight - $inputHeight;
            $top = $offset / 2;
        }

        imagecopyresampled($finalImage, //Destination Image 
            $inputImage, //Original Image 
            0, 0, //Destination top left Coord 
            $left, $top, //Source top left coord
            $this->_width, $this->_height,  //Final location Bottom Right Coord
            $inputWidth, $inputHeight //Source bottom right coord.
        );

    } else {

        if($inputRatio < $ratio) {
            //Original image is too wide, use $height as is. Modify $width;

            $scale = $inputHeight / $this->_height;


            $calculatedWidth = round(($inputWidth / $scale), 0);
            $calculatedHeight = $this->_height;

            $offset = $this->_width - $calculatedWidth;
            $left = round(($offset / 2), 0);
            $top = 0;

        } else {
            //Original image is too high, use $width as is.  Modify $height;
            $scale = $inputWidth / $this->_width;
            $calculatedHeight = round(($inputHeight / $scale),0);
            $calculatedWidth = $this->_width;
            $offset = $this->_height - $calculatedHeight;
            $top = round(($offset / 2), 2);
        }

        imagecopyresampled($finalImage, //Destination Image 
            $inputImage, //Original Image 
            $left, $top, //Destination top left Coord 
            0, 0, //Source top left coord
            $calculatedWidth, $calculatedHeight,  //Final location Bottom Right Coord
            $inputWidth, $inputHeight //Source bottom right coord.
        );
    }



    imagejpeg($finalImage, null, 100);
    imagedestroy($inputImage);
    imagedestroy($finalImage);

}

問題は実際には _getImageSize の実装にあると思われます。

private function _getImageSize($data)
{
    $soi = unpack('nmagic/nmarker', $data);
    if ($soi['magic'] != 0xFFD8) return false;
    $marker = $soi['marker'];
    $data   = substr($data, 4);
    $done   = false;

    while(1) {
            if (strlen($data) === 0) return false;
            switch($marker) {
                    case 0xFFC0:
                            $info = unpack('nlength/Cprecision/nY/nX', $data);
                            return array($info['X'], $info['Y']);
                            break;

                    default:
                            $info   = unpack('nlength', $data);
                            $data   = substr($data, $info['length']);
                            $info   = unpack('nmarker', $data);
                            $marker = $info['marker'];
                            $data   = substr($data, 2);
                            break;
            }
     }
}

この問題の別の例は、http://www.angelaryan.com/gallery/Image/22で確認できます。この例では、データベースに保存されている画像ではなく、青い四角が表示されます。

4

2 に答える 2

3

アップロード後に画像を自動的に「再保存」してみてください

imagejpeg(imagecreatefromjpeg($filename),$filename,9);

これにより、元の Facebook 画像から不正なヘッダーまたは認識されないヘッダーが再作成されます。

于 2013-07-31T18:05:05.023 に答える
0

getimagesizefromstring生データを自分で解析する代わりに、関数を使用する必要があると思います。

list($width, $height, $type) = getimagesizefromstring($data);

画像を再保存すると、画質が低下するだけです。

于 2013-07-31T18:09:45.487 に答える