如何检查图像是否不完整(缺失数据)?
How to check if an image is incompleted (missing data)?
所以我的 Raspberry Pi 上有这个程序,可以定期将我的博客备份到上面。我目前 运行 手动。今天我的互联网连接非常慢,所以我在下载过程中终止了程序。但是它确实保存了它设法获得的所有下载数据,现在我的程序读取图像存在并跳过它。当然我可以删除它并让程序为我重新下载它,但我想确保以后不会再发生这种情况。
我在服务器端使用 PHP。我用来保存图像的命令是
copy($url, $path);
我正在做一个非常简单的检查文件是否存在。
if(!file_exists($path))
我服务器上的图片文件是PNG和JPG文件格式。
笨蛋,我忘了写我试过的东西。我发现了多个这样的问题,但他们的解决方案似乎不起作用。他们都声称在这些情况下 imagecreatefromtype($img)
应该 return 为假。
PHP 手册:
Returns an image resource identifier on success, FALSE on errors.
我得到 "Premature end of JPEG file" 似乎应该 returned false 但事实并非如此。它 return 与图像未损坏的值相同,
Resource id #6
如果有某种快速的方法来确定图像是否完整,那就太好了。
您可以下载到一个临时文件(在同一分区),并在下载完成后重命名该文件。由于重命名文件是一个原子操作 - 只要源和目标位于同一分区 - 这将确保图像有效。
像这样:
// Create a tempfile
$tempfile = tempnam("/path/to/tempfolder", "download");
// Download to a tempfile
$ret = copy($url, $tempfile);
if($ret) {
// Move tempfile to final location.
// This is an atomic operation (with the restrictions named above)
rename($tempfile, "path/to/image.png");
} else {
unlink($tempfile);
die("Download broken");
}
即使复制没有完成 - 由于停电或其他原因,最终图像也不会在损坏的状态下创建。
在您的问题中,您使用了 PNG 图像,它具有您可以检查的校验和 (CRC32)。
如果校验和通过,那么很可能图像是完整的。
我想我想出了一个解决方案。这是一个应该检测不完整图像文件的代码。 仅支持 PNG 和 JPEG 格式,因为我目前不需要其他格式支持。它通过检查 JPEG 的 SOI 和 EOI 以及 PNG 的 IDHR 和 IEND 来工作。
您可以向此函数传递两个参数 - 文件名以及 JPEG 本身是否有更多 JPEG。
如果在调用函数时指定了$jpeg_in_jpeg
,则会执行较慢的脚本来检查SOI计数是否等于EOI计数,因此文件是完整的。虽然只有当文件以内部 JPEG 的 EOI 结束时才需要这样做,但你需要非常不幸才会发生这种情况。
Update:我意识到将所有数据解析为字节和数组然后比较值非常慢。一张 1.145 MB 的 JPEG 图像将在整整 26 秒内完成检查!但现在我将其更改为 preg_match_all()
,现在速度快了约 800 倍。有问题的数字是(以秒为单位):
26,64180707931471(旧方法)/ 0.032716035842896(新方法)= 814,3348175570528(快倍)。
如果您确实需要速度并且不认为您会不幸地以内部 JPEG 的 EOI 结尾,请使用不指定 $jpeg_in_jpeg
的更快方法。如果与新的 $jpeg_in_jpeg
方法相比,速度将提高约 2.6 倍。数字是(以秒为单位):
0.032716035842896 ($jpeg_in_jpeg = true
) / 0.012523889541626($jpeg_in_jpeg
未指定)= 2,612290353907259(快倍)
请记住,这都是在 Raspberry Pi B 模型上测试的。该函数的执行时间在普通服务器上应该会短很多。
function isImageComplete($file_name, $jpeg_in_jpeg = null){
$image_type = @exif_imagetype($file_name);
if($image_type)
$data = file_get_contents($file_name);
if($image_type == IMAGETYPE_JPEG){
if($jpeg_in_jpeg){
#Note: Some JPEG images have even more JPEGs inside of them (have multiple SOI and EOI). This check is slow, though eliminates the very small chance of detecting thumbnail's EOI as the file's ending.
$soi = chr(255).chr(216);
$eoi = chr(255).chr(217);
$results = preg_match_all("/$soi|$eoi/", $data, $out, PREG_PATTERN_ORDER);
$soi_count = 0;
$eoi_count = 0;
foreach($out[0] as $o)
if(ord($o[0]).ord($o[1]) == "255216")
$soi_count++;
elseif(ord($o[0]).ord($o[1]) == "255217")
$eoi_count++;
if($soi_count == $eoi_count && $soi_count > 1)
return 1;
else
return 0;
}
else{
$soi = substr($data, 0, 2);
$eoi = substr($data, -2);
$pair_count = 0;
if(ord($soi[0]).ord($soi[1]) == "255216")
$pair_count++;
if(ord($eoi[0]).ord($eoi[1]) == "255217")
$pair_count++;
if($pair_count == 2)
return 1;
else
return 0;
}
}
elseif($image_type == IMAGETYPE_PNG) {
$a_idhr = array();
$a_iend = array();
$idhr = substr($data, 0, 8);
$iend = substr($data, -12);
foreach(str_split($idhr) as $char){
array_push($a_idhr, ord($char));
}
foreach(str_split($iend) as $char){
array_push($a_iend, ord($char));
}
if(implode('', $a_idhr) == '13780787113102610' && implode('', $a_iend) == '0000736978681746696130')
return 1;
else
return 0;
}
else{
return -1; #File format not supported by the function.
}
}
在玩我的 RPi 时,我发现 JPEG 文件中有另一个 JPEG 图像,尽管 exif_thumbnail()
没有 return 任何东西。我认为您可以尝试检查 exif_thumbnail()
return 是否存在,然后使用较慢的 $jpeg_in_jpeg
。但正如我发现的那样,它没有 return 内部的 JPEG。可能是它没有被视为缩略图,而是被视为其他东西。请记住,这是我第一次深入研究图像文件格式,所以我知道的很少。
所以我的 Raspberry Pi 上有这个程序,可以定期将我的博客备份到上面。我目前 运行 手动。今天我的互联网连接非常慢,所以我在下载过程中终止了程序。但是它确实保存了它设法获得的所有下载数据,现在我的程序读取图像存在并跳过它。当然我可以删除它并让程序为我重新下载它,但我想确保以后不会再发生这种情况。
我在服务器端使用 PHP。我用来保存图像的命令是
copy($url, $path);
我正在做一个非常简单的检查文件是否存在。
if(!file_exists($path))
我服务器上的图片文件是PNG和JPG文件格式。
笨蛋,我忘了写我试过的东西。我发现了多个这样的问题,但他们的解决方案似乎不起作用。他们都声称在这些情况下 imagecreatefromtype($img)
应该 return 为假。
PHP 手册:
Returns an image resource identifier on success, FALSE on errors.
我得到 "Premature end of JPEG file" 似乎应该 returned false 但事实并非如此。它 return 与图像未损坏的值相同,
Resource id #6
如果有某种快速的方法来确定图像是否完整,那就太好了。
您可以下载到一个临时文件(在同一分区),并在下载完成后重命名该文件。由于重命名文件是一个原子操作 - 只要源和目标位于同一分区 - 这将确保图像有效。
像这样:
// Create a tempfile
$tempfile = tempnam("/path/to/tempfolder", "download");
// Download to a tempfile
$ret = copy($url, $tempfile);
if($ret) {
// Move tempfile to final location.
// This is an atomic operation (with the restrictions named above)
rename($tempfile, "path/to/image.png");
} else {
unlink($tempfile);
die("Download broken");
}
即使复制没有完成 - 由于停电或其他原因,最终图像也不会在损坏的状态下创建。
在您的问题中,您使用了 PNG 图像,它具有您可以检查的校验和 (CRC32)。
如果校验和通过,那么很可能图像是完整的。
我想我想出了一个解决方案。这是一个应该检测不完整图像文件的代码。 仅支持 PNG 和 JPEG 格式,因为我目前不需要其他格式支持。它通过检查 JPEG 的 SOI 和 EOI 以及 PNG 的 IDHR 和 IEND 来工作。
您可以向此函数传递两个参数 - 文件名以及 JPEG 本身是否有更多 JPEG。
如果在调用函数时指定了$jpeg_in_jpeg
,则会执行较慢的脚本来检查SOI计数是否等于EOI计数,因此文件是完整的。虽然只有当文件以内部 JPEG 的 EOI 结束时才需要这样做,但你需要非常不幸才会发生这种情况。
Update:我意识到将所有数据解析为字节和数组然后比较值非常慢。一张 1.145 MB 的 JPEG 图像将在整整 26 秒内完成检查!但现在我将其更改为 preg_match_all()
,现在速度快了约 800 倍。有问题的数字是(以秒为单位):
26,64180707931471(旧方法)/ 0.032716035842896(新方法)= 814,3348175570528(快倍)。
如果您确实需要速度并且不认为您会不幸地以内部 JPEG 的 EOI 结尾,请使用不指定 $jpeg_in_jpeg
的更快方法。如果与新的 $jpeg_in_jpeg
方法相比,速度将提高约 2.6 倍。数字是(以秒为单位):
0.032716035842896 ($jpeg_in_jpeg = true
) / 0.012523889541626($jpeg_in_jpeg
未指定)= 2,612290353907259(快倍)
请记住,这都是在 Raspberry Pi B 模型上测试的。该函数的执行时间在普通服务器上应该会短很多。
function isImageComplete($file_name, $jpeg_in_jpeg = null){
$image_type = @exif_imagetype($file_name);
if($image_type)
$data = file_get_contents($file_name);
if($image_type == IMAGETYPE_JPEG){
if($jpeg_in_jpeg){
#Note: Some JPEG images have even more JPEGs inside of them (have multiple SOI and EOI). This check is slow, though eliminates the very small chance of detecting thumbnail's EOI as the file's ending.
$soi = chr(255).chr(216);
$eoi = chr(255).chr(217);
$results = preg_match_all("/$soi|$eoi/", $data, $out, PREG_PATTERN_ORDER);
$soi_count = 0;
$eoi_count = 0;
foreach($out[0] as $o)
if(ord($o[0]).ord($o[1]) == "255216")
$soi_count++;
elseif(ord($o[0]).ord($o[1]) == "255217")
$eoi_count++;
if($soi_count == $eoi_count && $soi_count > 1)
return 1;
else
return 0;
}
else{
$soi = substr($data, 0, 2);
$eoi = substr($data, -2);
$pair_count = 0;
if(ord($soi[0]).ord($soi[1]) == "255216")
$pair_count++;
if(ord($eoi[0]).ord($eoi[1]) == "255217")
$pair_count++;
if($pair_count == 2)
return 1;
else
return 0;
}
}
elseif($image_type == IMAGETYPE_PNG) {
$a_idhr = array();
$a_iend = array();
$idhr = substr($data, 0, 8);
$iend = substr($data, -12);
foreach(str_split($idhr) as $char){
array_push($a_idhr, ord($char));
}
foreach(str_split($iend) as $char){
array_push($a_iend, ord($char));
}
if(implode('', $a_idhr) == '13780787113102610' && implode('', $a_iend) == '0000736978681746696130')
return 1;
else
return 0;
}
else{
return -1; #File format not supported by the function.
}
}
在玩我的 RPi 时,我发现 JPEG 文件中有另一个 JPEG 图像,尽管 exif_thumbnail()
没有 return 任何东西。我认为您可以尝试检查 exif_thumbnail()
return 是否存在,然后使用较慢的 $jpeg_in_jpeg
。但正如我发现的那样,它没有 return 内部的 JPEG。可能是它没有被视为缩略图,而是被视为其他东西。请记住,这是我第一次深入研究图像文件格式,所以我知道的很少。