文件存到aws的S3后, 调用getimagesize失败分析

一、问题

将图片在windows下用图片查看器修改后,上传到s3中,调用getimagesize获取图片信息总是返回false,其它图片正常;

代码如下:

$fileName = ‘s3://sdk1/20150317/174290_1_1428371.JPG‘;
$size = getimagesize($fileName);
var_dump($size);

以上代码总是输出false。

即部分图片调用成功,部分调用失败;

图片上传后,可以对上传的临时文件调用getimagesize获取图片信息;

PHP的版本为5.6.2

二、分析过程:

1、准备知识

先说下PHP中对流的封装,通过函数stream_wrapper_register,我们可以注册一个新的URL,像上面的s3开头的文件;

AWS封装了一套对这类文件的操作,包括读、写、定位、打开目录等函数,这方面资料可以看下PHP文档,AWS的封装代码可以看下AWS的PHP的SDK。

通过追代码,发现对S3文件的请求最终会转成一个HTTPS的请求,https://sdk1.s3.cn-north-1.amazonaws.com.cn/20150317/174290_1_1428371.JPG

2、代码追踪

看下getimagesize函数的实现,

这个是调用php_getimagesize_from_any来实现的,

重要函数为php_getimagesize_from_stream,看下这个函数:

会先读取文件的前三字节,获取文件类型,具体可以看下php_getimagetype函数;

如果是jpg类型的图片,会调用php_handle_jpeg函数,

其中函数php_next_marker会读取文件的二进制流当前字节,根据不同字节作不同的处理,

如果当前字节为宏M_APP0至M_APP15,即0xe0到0xef,表示是APP或变量;

出问题的图片当前是variable,看下php_skip_variables函数

如果当前是一个变量,则要跳过这个变量,读取下一块信息,这里调用php_stream_seek来进行seek操作,

看两块关键代码,上面一段表示seek的位置在已经读取的范围内;

下一块表示这个流不支持seek操作,返回失败;

在我们的例子里,上面表示正常情况,下面表示失败情况的;

我们再回到s3的seek操作的封装:

return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;

即不支持seek,所以失败了;

明白了吗,原理就是getimagesize为从文件的二进制流中读取图片信息,而这个信息是文件的位置是不固定的,必须一块一块的解析,正常的图片的相关信息是在文件前8192个字节之内,而

出错的是8192之外的,如果在8192之外的,需要先将文件流定位到相应位置,再从该位置读取信息,而s3开头的路径不支持seek操作,导致PHP读不文件元信息。

三、改进方法

改写Stream::seek方法

public function seek($offset, $whence = SEEK_SET)

{

if ($whence == SEEK_SET)

{

$pos = $this->ftell();

if ($pos < $offset)

{

$this->read($offset - $pos);

}

}

return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false;

时间: 2024-08-12 03:20:21

文件存到aws的S3后, 调用getimagesize失败分析的相关文章

TextView在xml文件中加入onClick属性后,clickable值依旧是false的原因。

先看View中是如何定义clickable和onClick的: case com.android.internal.R.styleable.View_clickable: if (a.getBoolean(attr, false)) { viewFlagValues |= CLICKABLE; viewFlagMasks |= CLICKABLE; } break; 上面这段代码是View源码中对clickable属性的定义,缺省值为false. case R.styleable.View_on

aws 的 s3 put_object vs upload_file

用过aws的人,都知道s3存储东西贼方便. 他的上传有两个方法, 第一个是get_object(),是将文件的内容赋值给body,进行上传,并设置存储桶为上传文件为公开 response = s3.put_object(Bucket=bucket_tmp, Key=file_key, Body=content, ACL="public-read-write") 然后点击对应的文件路径,就有一个下载的s3对象url进行下载,如下图所示,会自动弹出: 第二个是upload_file s3.

asserts文件存到外部SD卡里

layout代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_pare

Java-使用IO流对大文件进行分割和分割后的合并

有的时候我们想要操作的文件很大,比如:我们想要上传一个大文件,但是收到上传文件大小的限制,无法上传,这是我们可以将一个大的文件分割成若干个小文件进行操作,然后再把小文件还原成源文件.分割后的每个小文件的类型可以自己定义. 一下是我编写的一个大文件的分割和合并的代码: package com.lym; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; impor

【ThinkingInC++】5、吧文件中的单词由后向前打印

文本文件 i like china i can play just do it i will cometrue my dream come on!!! 源程序 /** * 功能:2-5,2-6,2-7吧文件中的单词由后向前打印 * 时间:2014年7月30日21:27:12 * 作者:cutter_point */ #include<iostream> #include<stdlib.h> #include<fstream> #include<vector>

为什么需要在TypedArray后调用recycle

当我们没有在使用TypedArray后调用recycle,编译器会提示“This TypedArray should be recycled after use with #recycle()”. 官方的解释是:回收TypedArray,以便后面重用.在调用这个函数后,你就不能再使用这个TypedArray. 在TypedArray后调用recycle主要是为了缓存.当recycle被调用后,这就说明这个对象从现在可以被重用了.TypedArray 内部持有部分数组,它们缓存在Resources

tcp 在调用connect失败后要不要重新socket

tcp 在调用connect失败后要不要重新socket http://blog.csdn.net/occupy8/article/details/48253251

如何把js文件编译成dll供页面调用

1. 在解决方案中添加一个项目:JSControl 2. 在这个项目添加一个js文件(JScript1.js) 脚本的内容: function showAlert(){ alert('Today is a good dary'); } 3. 改变JScript1.js的属性,Build Action为Embedded Resource(嵌入的资源) 4. 在JSControl项目的AssemblyInfo.cs文件中添加一行:(注意JSControl.JScript1.js,JSControl是

oracle修改后的tnsnames文件无法保存,保存后提示另存为,怎么回事

在配置新的数据库的时候,需要配置监听把一些信息放进去,找到tnsnames时进行修改,文件无法保存,保存后提示另存为. 后来经过在网上查找资料,这种现象产生的原因可能是因为该文件被加密过,所以不允许修改.解决的方法如下: 1.首先打开tnsnames.ora文件 2.右键该文件,点击属性,并进入到常规选项中 3.点击"安全页签",可以选择"编辑"按钮,然后进入到新的界面 4.最后将写入操作的"允许"列的勾上,并点击应用,即可以将tnsnames.