├── .gitignore ├── config ├── README.md └── ngx_http_image_module.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | 4 | # Libraries 5 | *.lib 6 | *.a 7 | 8 | # Shared objects (inc. Windows DLLs) 9 | *.dll 10 | *.so 11 | *.so.* 12 | *.dylib 13 | 14 | # Executables 15 | *.exe 16 | *.out 17 | *.app 18 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_feature="http_image_module" 2 | ngx_feature_name= 3 | ngx_feature_run=no 4 | ngx_feature_incs="#include 5 | #include 6 | #include " 7 | ngx_feature_path= 8 | ngx_feature_libs="-lcurl" 9 | ngx_feature_test= 10 | 11 | ngx_addon_name=ngx_http_image_module 12 | HTTP_MODULES="$HTTP_MODULES ngx_http_image_module" 13 | CORE_INCS="$CORE_INCS \ 14 | $ngx_addon_dir" 15 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ 16 | ${ngx_addon_dir}/ngx_http_image_module.c" 17 | USE_LIBGD=YES 18 | have=NGX_HTTP_HEADERS . auto/have 19 | . auto/feature 20 | 21 | 22 | if [ $ngx_found = yes ]; then 23 | 24 | CORE_INCS="$CORE_INCS $ngx_feature_path" 25 | CORE_LIBS="$CORE_LIBS $ngx_feature_libs" 26 | 27 | else 28 | 29 | CORE_INCS="$CORE_INCS $ngx_feature_path" 30 | CORE_LIBS="$CORE_LIBS $ngx_feature_libs" 31 | 32 | fi 33 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nginx Image 缩略图 模块 2 | 3 | 4 | ### 模块同时支持 Nginx 和 tengine 5 | 6 | - 本nginx模块主要功能是对请求的图片进行缩略/水印处理,支持文字水印和图片水印。 7 | - 支持自定义字体,文字大小,水印透明度,水印位置。 8 | - 判断原图是否是否大于指定尺寸才处理。 9 | ....等等 10 | 11 | 12 | ## 编译方法 13 | 14 | 编译前请确认您的系统已经安装了libcurl-dev libgd2-dev libpcre-dev 依赖库 15 | 16 | ### Debian / Ubuntu 系统举例 17 | ```bash 18 | # 如果你没有安装GCC相关环境才需要执行 19 | $ sudo apt-get install build-essential m4 autoconf automake make 20 | $ sudo apt-get install libgd2-noxpm-dev libcurl4-openssl-dev libpcre3-dev 21 | ``` 22 | 23 | ### CentOS /RedHat / Fedora 24 | ```bash 25 | # 请确保已经安装了gcc automake autoconf m4 26 | $ sudo yum install gd-devel pcre-devel libcurl-devel 27 | ``` 28 | 29 | ### FreeBSD / NetBSD / OpenBSD 30 | ``` 31 | # 不多说了,自己用port 把libcurl-dev libgd2-dev libpcre-dev 装上吧 32 | # 编译前请确保已经安装gcc automake autoconf m4 33 | ``` 34 | 35 | ### Windows 36 | ``` 37 | # 也支持的,不过要修改的代码太多了,包括Nginx本身,用VC++来编译 38 | # 嫌麻烦可以用cygwin来编译。还是不建议你这么做了,用Unix/Linux操作系统吧。 39 | ``` 40 | 41 | ###下载nginx / tengine 源代码 42 | 43 | #### 然后下载本模块代码,并放在nginx源代码目录下 44 | #### 选Nginx还是Tengine,您自己看,两者选其一 45 | 46 | ```bash 47 | # 下载Tengine 48 | $ wget http://tengine.taobao.org/download/tengine-1.4.5.tar.gz 49 | $ tar -zxvf tengine-1.4.5.tar.gz 50 | $ cd tengine-1.4.5 51 | ``` 52 | 53 | ```bash 54 | # 下载Nginx 55 | $ wget http://nginx.org/download/nginx-1.4.0.tar.gz 56 | $ tar -zxvf nginx-1.4.0.tar.gz 57 | $ cd nginx-1.4.0 58 | ``` 59 | 60 | ```bash 61 | $ wget https://github.com/oupula/ngx_image_thumb/archive/master.zip 62 | $ unzip master.zip 63 | $ ./configure --add-module=./nginx-image-master 64 | $ make 65 | $ sudo make install 66 | ``` 67 | 68 | ## 配置方法 69 | 70 | 打开 `nginx.conf` 71 | 72 | ```bash 73 | vim /etc/nginx/nginx.conf 74 | # 该路径为默认路径,如果不在此处,自己找一下 75 | ``` 76 | 77 | 在 78 | ```apache 79 | location / { 80 | root html; 81 | #添加以下配置 82 | image on; 83 | image_output on; 84 | } 85 | ``` 86 | 87 | 或者指定目录开启 88 | ```apache 89 | location /upload { 90 | root html; 91 | image on; 92 | image_output on; 93 | } 94 | ``` 95 | 96 | 97 | ## 其他参数说明: 98 | ```apache 99 | image on/off 是否开启缩略图功能,默认关闭 100 | 101 | image_backend on/off 是否开启镜像服务,当开启该功能时,请求目录不存在的图片(判断原图),将自动从镜像服务器地址下载原图 102 | 103 | image_backend_server 镜像服务器地址 104 | 105 | image_output on/off 是否不生成图片而直接处理后输出 默认off 106 | 107 | image_jpeg_quality 75 生成JPEG图片的质量 默认值75 108 | 109 | image_water on/off 是否开启水印功能 110 | 111 | image_water_type 0/1 水印类型 0:图片水印 1:文字水印 112 | 113 | image_water_min 300 300 图片宽度 300 高度 300 的情况才添加水印 114 | 115 | image_water_pos 0-9 水印位置 默认值9 0为随机位置,1为顶端居左,2为顶端居中,3为顶端居右,4为中部居左,5为中部居中,6为中部居右,7为底端居左,8为底端居中,9为底端居右 116 | 117 | image_water_file 水印文件(jpg/png/gif),绝对路径或者相对路径的水印图片 118 | 119 | image_water_transparent 水印透明度,默认20 120 | 121 | image_water_text 水印文字 "Power By Vampire" 122 | 123 | image_water_font_size 水印大小 默认 5 124 | 125 | image_water_font 文字水印字体文件路径 126 | 127 | image_water_color 水印文字颜色,默认 #000000 128 | ``` 129 | 130 | ## 调用说明 131 | 132 | 这里假设你的nginx 访问地址为 `http://127.0.0.1/` 133 | 134 | 并在nginx网站根目录存在一个 `test.jpg` 的图片 135 | 136 | 通过访问 137 | 138 | `http://127.0.0.1/test.jpg!c300x200.jpg` 将会 生成/输出 `test.jpg` **300x200** 的缩略图 139 | 140 | 其中 **c** 是生成图片缩略图的参数, **300** 是生成缩略图的 **宽度** **200** 是生成缩略图的 **高度** 141 | 142 | 一共可以生成**四**种不同类型的缩略图。 143 | 144 | 支持 jpeg / png / gif (Gif生成后变成静态图片) 145 | 146 | 147 | **C** 参数按请求宽高比例从图片高度 **10%** 处开始截取图片,然后缩放/放大到指定尺寸( **图片缩略图大小等于请求的宽高** ) 148 | 149 | **M** 参数按请求宽高比例居中截图图片,然后缩放/放大到指定尺寸( **图片缩略图大小等于请求的宽高** ) 150 | 151 | **T** 参数按请求宽高比例按比例缩放/放大到指定尺寸( **图片缩略图大小可能小于请求的宽高** ) 152 | 153 | **W** 参数按请求宽高比例缩放/放大到指定尺寸,空白处填充白色背景颜色( **图片缩略图大小等于请求的宽高** ) 154 | 155 | 156 | 157 | ## 调用举例 158 | 159 | - http://oopul.vicp.net/12.jpg!c300x300.jpg 160 | 161 | - http://oopul.vicp.net/12.jpg!t300x300.jpg 162 | 163 | - http://oopul.vicp.net/12.jpg!m300x300.jpg 164 | 165 | - http://oopul.vicp.net/12.jpg!w300x300.jpg 166 | 167 | - http://oopul.vicp.net/12.c300x300.jpg 168 | 169 | - http://oopul.vicp.net/12.t300x300.jpg 170 | 171 | - http://oopul.vicp.net/12.m300x300.jpg 172 | 173 | - http://oopul.vicp.net/12.w300x300.jpg 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /ngx_http_image_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Vampire 3 | * 4 | * {根据URL生成缩略图/添加水印} 5 | * 6 | * nginx.conf 配置值 7 | * image on/off 是否开启缩略图功能,默认关闭 8 | * image_backend on/off 是否开启镜像服务 9 | * image_backend_server 镜像服务器地址 10 | * image_output on/off 是否不生成图片而直接处理后输出 默认off 11 | * image_jpeg_quality 75 生成JPEG图片的质量 默认值75 12 | * image_water on/off 是否开启水印功能 13 | * image_water_type 0/1 水印类型 0:图片水印 1:文字水印 14 | * image_water_min 300 300 图片宽度 300 高度 300 的情况才添加水印 15 | * image_water_pos 0-9 水印位置 默认值9 0为随机位置,1为顶端居左,2为顶端居中,3为顶端居右,4为中部居左,5为中部居中,6为中部居右,7为底端居左,8为底端居中,9为底端居右 16 | * image_water_file 水印文件(jpg/png/gif),绝对路径或者相对路径的水印图片 17 | * image_water_transparent 水印透明度,默认20 18 | * image_water_text 水印文字 "Power By Vampire" 19 | * image_water_font_size 水印大小 默认 5 20 | * image_water_font;//文字水印字体文件路径 21 | * image_water_color 水印文字颜色,默认 #000000 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "ngx_http_request.h" 36 | 37 | #define NGX_IMAGE_NONE 0 38 | #define NGX_IMAGE_JPEG 1 39 | #define NGX_IMAGE_GIF 2 40 | #define NGX_IMAGE_PNG 3 41 | #define NGX_IMAGE_BMP 4 42 | 43 | #define MAX_DIR_PATH_LEN 256 44 | 45 | #ifndef WIN32 46 | #define stricmp strcasecmp 47 | #endif 48 | 49 | typedef struct 50 | { 51 | ngx_flag_t image_status;//是否打开图片处理 52 | char * url;//请求URL地址 53 | char request_dir[MAX_DIR_PATH_LEN];//URL目录 54 | char * request_source;//URL源文件URL 55 | char * request_filename;//URL中的文件名 56 | char local_dir[MAX_DIR_PATH_LEN];//当前WEB目录 57 | char * extension;//目标图片后缀名 (png/gif/jpg/jpeg/jpe) 58 | char * m_type;//生成缩略图的方式 缩放/居中缩放/顶部10%开始缩放 59 | char * source_file;//原始图片路径 60 | char * dest_file;//目标图片路径 61 | u_char * img_data;//图片内容 62 | char buffer[10][255]; 63 | gdImagePtr src_im;//原始图片GD对象 64 | gdImagePtr dst_im;//目标图片GD对象 65 | gdImagePtr w_im;//补白边图片GD对象 66 | int w_margin;//是否对图片补白边 67 | int img_size;//图片大小 68 | int pcre_type;//图片正则匹配类型 0为新规则(test.jpg!c300x300.jpg) 1为旧规则(test.c300x300.jpg) 69 | int header_type;//HTTP头部类型 70 | int max_width;//目标图片最大宽度 71 | int max_height;//目标图片最大宽度 72 | int src_type;//原始图片类型 73 | int dest_type;//目标图片类型 74 | int src_width;//原始图片宽度 75 | int src_height;//原始图片高度 76 | int src_x;//原始图片X坐标 77 | int src_y;//原始图片Y坐标 78 | int src_w;//原始图片宽度 79 | int src_h;//原始图片高度 80 | int width;//目标图片宽度 81 | int height;//目标图片高度 82 | int dst_x;//目标图片X坐标 83 | int dst_y;//目标图片Y坐标 84 | ngx_flag_t image_output;//是否不保存图片直接输出图片内容给客户端 默认off 85 | int jpeg_quality;//JPEG图片质量 默认75 86 | gdImagePtr water_im;//水印图片GD对象 87 | ngx_flag_t water_status;//是否打开水印功能 默认关闭 88 | int water_im_type;//水印图片类型 89 | int water_type;//水印类型 0:图片水印 1:文字水印 90 | int water_pos;//水印位置 91 | int water_transparent;//水印透明度 92 | int water_width_min;//原图小于该宽度的图片不添加水印 93 | int water_height_min;//原图小于该高度的图片不添加水印 94 | ngx_flag_t backend;//是否请求原始服务器 95 | ngx_str_t backend_server;//图片原始服务器,如请求的图片不存在,从该服务器下载 96 | ngx_str_t water_image;//水印图片 97 | ngx_str_t water_text;//水印文字内容 98 | int water_font_size;//水印文字大小 99 | ngx_str_t water_font;//文字水印字体文件路径 100 | ngx_str_t water_color;//水印文字颜色 (#0000000) 101 | ngx_http_request_t *request;//HTTP请求源 102 | } ngx_image_conf_t; 103 | 104 | static FILE *curl_handle; 105 | 106 | static ngx_str_t ngx_http_image_types[] = 107 | { 108 | ngx_string("text/html"), 109 | ngx_string("image/jpeg"), 110 | ngx_string("image/gif"), 111 | ngx_string("image/png") 112 | }; 113 | 114 | static char *ngx_http_image(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 115 | static void *ngx_http_image_create_loc_conf(ngx_conf_t *cf); 116 | static char *ngx_http_image_merge_loc_conf(ngx_conf_t *cf,void *parent, void *child); 117 | static char * ngx_http_image_water_min(ngx_conf_t *cf, ngx_command_t *cmd,void *conf); 118 | static ngx_uint_t ngx_http_image_value(ngx_str_t *value); 119 | char * ngx_conf_set_number_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 120 | char * ngx_conf_set_string_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 121 | static ngx_int_t output(ngx_http_request_t *r,void *conf,ngx_str_t type); 122 | static void gd_clean_data(void *data);//清除GD DATA数据 123 | static void make_thumb(void *conf);//创建GD对象缩略图,缩略图在此函数中已经处理好,但没有写入到文件 124 | static void water_mark(void *conf);//给图片打上水印 125 | static void thumb_to_string(void *conf);//GD对象数据转换为二进制字符串 126 | static int parse_image_info(void *conf);//根据正则获取基本的图片信息 127 | static int calc_image_info(void *conf);//根据基本图片信息计算缩略图信息 128 | static void check_image_type(void *conf);//判断图片类型 129 | static char * get_ext(char *filename);//根据文件名取图片类型 130 | static int get_ext_header(char *filename);//根据文件头取图片类型 131 | static int file_exists(char *filename);//判断文件是否存在 132 | static int read_img(char **filename,int * size,void ** buffer, ngx_http_request_t *request);//图片读入函数 133 | static void write_img(void * conf);//图片保存到文件 134 | static void water_image_from(void * conf);//创建水印图片GD对象 135 | static void image_from(void * conf);//创建原图GD库对象 136 | static int get_header(char *url);//取得远程URL的返回值 137 | static size_t curl_get_data(void *ptr, size_t size, size_t nmemb, void *stream);//CURL调用函数 138 | static int create_dir(char *dir);//递归创建目录 139 | static void get_request_source(void *conf); 140 | static void dirname(char *path,char *dirpath);//根据URL获取目录路径 141 | static void download(void * conf);//下载文件 142 | 143 | static int ngx_log(const char *format, ...); 144 | 145 | //日志模式 146 | #define LOG_MODE_DISABLE 0 //不输出日志 147 | #define LOG_MODE_CONSOLE 1 //日志输出到控制台 148 | #define LOG_MODE_FILE 2 //日志输出到文件 149 | #define LOG_MODE LOG_MODE_DISABLE 150 | 151 | #if LOG_MODE == LOG_MODE_FILE 152 | #define PRINT_LOG(level, log, ...) ngx_log_error(level, log, 0, __VA_ARGS__) 153 | #else 154 | #define PRINT_LOG(level, log, ...) ngx_log(__VA_ARGS__) 155 | #endif 156 | 157 | 158 | 159 | static ngx_command_t ngx_http_image_commands[] = 160 | { 161 | { 162 | ngx_string("image"), 163 | NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 164 | ngx_http_image, 165 | NGX_HTTP_LOC_CONF_OFFSET, 166 | 0, 167 | NULL 168 | }, 169 | { 170 | ngx_string("image_output"), 171 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 172 | ngx_conf_set_flag_slot, 173 | NGX_HTTP_LOC_CONF_OFFSET, 174 | offsetof(ngx_image_conf_t, image_output), 175 | NULL 176 | }, 177 | { 178 | ngx_string("image_backend"), 179 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 180 | ngx_conf_set_flag_slot, 181 | NGX_HTTP_LOC_CONF_OFFSET, 182 | offsetof(ngx_image_conf_t, backend), 183 | NULL 184 | }, 185 | { 186 | ngx_string("image_backend_server"), 187 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 188 | ngx_conf_set_str_slot, 189 | NGX_HTTP_LOC_CONF_OFFSET, 190 | offsetof(ngx_image_conf_t, backend_server), 191 | NULL 192 | }, 193 | { 194 | ngx_string("image_water"), 195 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 196 | ngx_conf_set_flag_slot, 197 | NGX_HTTP_LOC_CONF_OFFSET, 198 | offsetof(ngx_image_conf_t, water_status), 199 | NULL 200 | }, 201 | { 202 | ngx_string("image_water_type"), 203 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ARGS_NUMBER, 204 | ngx_conf_set_number_slot, 205 | NGX_HTTP_LOC_CONF_OFFSET, 206 | offsetof(ngx_image_conf_t, water_type), 207 | NULL 208 | }, 209 | { 210 | ngx_string("image_water_min"), 211 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 212 | ngx_http_image_water_min, 213 | NGX_HTTP_LOC_CONF_OFFSET, 214 | 0, 215 | NULL 216 | }, 217 | { 218 | ngx_string("image_water_pos"), 219 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ARGS_NUMBER, 220 | ngx_conf_set_number_slot, 221 | NGX_HTTP_LOC_CONF_OFFSET, 222 | offsetof(ngx_image_conf_t, water_pos), 223 | NULL 224 | }, 225 | { 226 | ngx_string("image_water_file"), 227 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 228 | ngx_conf_set_str_slot, 229 | NGX_HTTP_LOC_CONF_OFFSET, 230 | offsetof(ngx_image_conf_t, water_image), 231 | NULL 232 | }, 233 | { 234 | ngx_string("image_water_transparent"), 235 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 236 | ngx_conf_set_number_slot, 237 | NGX_HTTP_LOC_CONF_OFFSET, 238 | offsetof(ngx_image_conf_t, water_transparent), 239 | NULL 240 | }, 241 | { 242 | ngx_string("image_water_text"), 243 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 244 | ngx_conf_set_str_slot, 245 | NGX_HTTP_LOC_CONF_OFFSET, 246 | offsetof(ngx_image_conf_t, water_text), 247 | NULL 248 | }, 249 | { 250 | ngx_string("image_water_font"), 251 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 252 | ngx_conf_set_str_slot, 253 | NGX_HTTP_LOC_CONF_OFFSET, 254 | offsetof(ngx_image_conf_t, water_font), 255 | NULL 256 | }, 257 | { 258 | ngx_string("image_water_font_size"), 259 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ARGS_NUMBER, 260 | ngx_conf_set_number_slot, 261 | NGX_HTTP_LOC_CONF_OFFSET, 262 | offsetof(ngx_image_conf_t, water_font_size), 263 | NULL 264 | }, 265 | { 266 | ngx_string("image_water_color"), 267 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 268 | ngx_conf_set_str_slot, 269 | NGX_HTTP_LOC_CONF_OFFSET, 270 | offsetof(ngx_image_conf_t, water_color), 271 | NULL 272 | }, 273 | { 274 | ngx_string("image_jpeg_quality"), 275 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ARGS_NUMBER, 276 | ngx_conf_set_number_slot, 277 | NGX_HTTP_LOC_CONF_OFFSET, 278 | offsetof(ngx_image_conf_t, jpeg_quality), 279 | NULL 280 | }, 281 | ngx_null_command 282 | }; 283 | 284 | 285 | static ngx_http_module_t ngx_http_image_module_ctx = 286 | { 287 | NULL, /* preconfiguration */ 288 | NULL, /* postconfiguration */ 289 | 290 | NULL, /* create main configuration */ 291 | NULL, /* init main configuration */ 292 | 293 | NULL, /* create server configuration */ 294 | NULL, /* merge server configuration */ 295 | 296 | ngx_http_image_create_loc_conf, /* create location configuration */ 297 | ngx_http_image_merge_loc_conf /* merge location configuration */ 298 | }; 299 | 300 | 301 | ngx_module_t ngx_http_image_module = 302 | { 303 | NGX_MODULE_V1, 304 | &ngx_http_image_module_ctx, /* module context */ 305 | ngx_http_image_commands, /* module directives */ 306 | NGX_HTTP_MODULE, /* module type */ 307 | NULL, /* init master */ 308 | NULL, /* init module */ 309 | NULL, /* init process */ 310 | NULL, /* init thread */ 311 | NULL, /* exit thread */ 312 | NULL, /* exit process */ 313 | NULL, /* exit master */ 314 | NGX_MODULE_V1_PADDING 315 | }; 316 | 317 | static int 318 | ngx_log(const char *format, ...) 319 | { 320 | #if LOG_MODE == LOG_MODE_DISABLE 321 | return 0; 322 | #endif 323 | 324 | int done; 325 | va_list arg; 326 | 327 | //打印临时日志 328 | time_t rawtime; 329 | struct tm * timeinfo; 330 | time ( &rawtime ); 331 | timeinfo = localtime ( &rawtime ); 332 | printf ( "[INFO_Image] %04d.%02d.%02d %02d:%02d:%02d ", 333 | timeinfo->tm_year+1900, timeinfo->tm_mon+1, timeinfo->tm_mday, 334 | timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec ); 335 | 336 | va_start (arg, format); 337 | done = vfprintf (stdout, format, arg); 338 | va_end (arg); 339 | return done; 340 | }; 341 | 342 | static void * 343 | ngx_http_image_create_loc_conf(ngx_conf_t *cf) 344 | { 345 | PRINT_LOG(NGX_LOG_ERR, cf->log, "FUNC ngx_http_image_create_loc_conf \n"); 346 | ngx_image_conf_t *conf; 347 | 348 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_image_conf_t)); 349 | if (conf == NULL) 350 | { 351 | return NULL; 352 | } 353 | conf->image_status = NGX_CONF_UNSET; 354 | conf->backend = NGX_CONF_UNSET; 355 | conf->jpeg_quality = NGX_CONF_UNSET; 356 | conf->image_output = NGX_CONF_UNSET; 357 | conf->water_status = NGX_CONF_UNSET; 358 | conf->water_type = NGX_CONF_UNSET; 359 | conf->water_pos = NGX_CONF_UNSET; 360 | conf->water_transparent = NGX_CONF_UNSET; 361 | conf->water_width_min = NGX_CONF_UNSET; 362 | conf->water_height_min = NGX_CONF_UNSET; 363 | conf->water_font_size = NGX_CONF_UNSET; 364 | return conf; 365 | } 366 | 367 | static char * 368 | ngx_http_image_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 369 | { 370 | PRINT_LOG(NGX_LOG_ERR, cf->log, "FUNC ngx_http_image_merge_loc_conf \n"); 371 | ngx_image_conf_t *prev = parent; 372 | ngx_image_conf_t *conf = child; 373 | ngx_conf_merge_value(conf->image_status,prev->image_status,0); 374 | ngx_conf_merge_value(conf->backend,prev->backend,0); 375 | ngx_conf_merge_str_value(conf->backend_server,prev->backend_server,"http://image.oupula.org/"); 376 | ngx_conf_merge_value(conf->jpeg_quality,prev->jpeg_quality,75); 377 | ngx_conf_merge_value(conf->water_type,prev->water_type,0); 378 | ngx_conf_merge_value(conf->water_width_min,prev->water_width_min,0); 379 | ngx_conf_merge_value(conf->water_height_min,prev->water_height_min,0); 380 | ngx_conf_merge_value(conf->water_pos,prev->water_pos,9); 381 | ngx_conf_merge_value(conf->water_transparent,prev->water_transparent,20); 382 | ngx_conf_merge_str_value(conf->water_text,prev->water_text,"[ Copyright By Vampire ]"); 383 | ngx_conf_merge_value(conf->water_font_size,prev->water_font_size,5); 384 | ngx_conf_merge_str_value(conf->water_font,prev->water_font,"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"); 385 | ngx_conf_merge_str_value(conf->water_color,prev->water_color,"#000000"); 386 | return NGX_CONF_OK; 387 | } 388 | 389 | static ngx_int_t ngx_http_image_handler(ngx_http_request_t *r) 390 | { 391 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, "FUNC ngx_http_image_handler \n"); 392 | u_char *last; 393 | size_t root; 394 | ngx_int_t rc; 395 | ngx_str_t path; 396 | char request_uri[255]; 397 | int request_uri_len; 398 | ngx_image_conf_t *conf; 399 | 400 | conf = ngx_http_get_module_loc_conf(r, ngx_http_image_module); 401 | conf->request = r; 402 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) 403 | { 404 | return NGX_HTTP_NOT_ALLOWED; 405 | } 406 | if (r->headers_in.if_modified_since) 407 | { 408 | return NGX_HTTP_NOT_MODIFIED; 409 | } 410 | 411 | if (r->uri.data[r->uri.len - 1] == '/') 412 | { 413 | return NGX_DECLINED; 414 | } 415 | 416 | rc = ngx_http_discard_request_body(r); 417 | if (rc != NGX_OK) 418 | { 419 | return rc; 420 | } 421 | last = ngx_http_map_uri_to_path(r, &path, &root, 0); //从原服务器获取实际路径 422 | if (last == NULL) 423 | { 424 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 425 | } 426 | 427 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, "FUNC ngx_http_image_handler, path = %s\n", 428 | (char *) path.data); 429 | if(file_exists((char*) path.data) == -1) 430 | { 431 | request_uri_len = strlen((char *)r->uri_start) - strlen((char *)r->uri_end); 432 | strncpy(request_uri, (char *)r->uri_start, request_uri_len); 433 | request_uri[request_uri_len] = '\0'; 434 | dirname(request_uri,conf->request_dir); 435 | conf->url = request_uri;//请求的URL地址 436 | conf->dest_file = (char *)path.data; 437 | check_image_type(conf);//检查图片类型(根据后缀进行简单判断) 438 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, 439 | "FUNC ngx_http_image_handler, request_uri = %s\n", request_uri); 440 | if( conf->dest_type > 0 ) 441 | { 442 | if (parse_image_info(conf) == 0)//解析并处理请求的图片URL 443 | { 444 | 445 | make_thumb(conf);//生成图片缩略图 446 | water_mark(conf);//图片打上水印 447 | thumb_to_string(conf);//GD对象转换成二进制字符串 448 | if(conf->image_output == 0) 449 | { 450 | write_img(conf);//保存图片缩略图到文件 451 | } 452 | if(conf->image_output == 1) 453 | { 454 | return output(r,conf,ngx_http_image_types[conf->dest_type]); 455 | } 456 | } 457 | } 458 | } 459 | 460 | return NGX_DECLINED; 461 | } 462 | 463 | static char * ngx_http_image_water_min(ngx_conf_t *cf, ngx_command_t *cmd,void *conf) 464 | { 465 | PRINT_LOG(NGX_LOG_ERR, cf->log, "FUNC ngx_http_image_water_min \n"); 466 | ngx_image_conf_t *info = conf; 467 | ngx_str_t *value; 468 | ngx_http_complex_value_t cv; 469 | ngx_http_compile_complex_value_t ccv; 470 | value = cf->args->elts; 471 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 472 | ccv.cf = cf; 473 | ccv.value = &value[1]; 474 | ccv.complex_value = &cv; 475 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) 476 | { 477 | return NGX_CONF_ERROR; 478 | } 479 | if (cv.lengths == NULL) 480 | { 481 | info->water_width_min = (int)ngx_http_image_value(&value[1]); 482 | info->water_height_min = (int)ngx_http_image_value(&value[2]); 483 | } 484 | return NGX_CONF_OK; 485 | } 486 | 487 | static ngx_uint_t ngx_http_image_value(ngx_str_t *value) 488 | { 489 | ngx_int_t n; 490 | if (value->len == 1 && value->data[0] == '-') 491 | { 492 | return (ngx_uint_t) -1; 493 | } 494 | n = ngx_atoi(value->data, value->len); 495 | if (n > 0) 496 | { 497 | return (ngx_uint_t) n; 498 | } 499 | return 0; 500 | } 501 | 502 | char * ngx_conf_set_number_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 503 | { 504 | PRINT_LOG(NGX_LOG_ERR, cf->log, "FUNC ngx_conf_set_number_slot \n"); 505 | char *p = conf; 506 | int *np; 507 | ngx_str_t *value; 508 | np = (int *) (p + cmd->offset); 509 | if (*np != NGX_CONF_UNSET) 510 | { 511 | return "is duplicate"; 512 | } 513 | value = cf->args->elts; 514 | *np = (int)ngx_atoi(value[1].data, value[1].len); 515 | if (*np == NGX_ERROR) 516 | { 517 | return "invalid number"; 518 | } 519 | return NGX_CONF_OK; 520 | } 521 | 522 | static char * 523 | ngx_http_image(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 524 | { 525 | PRINT_LOG(NGX_LOG_ERR, cf->log, "FUNC ngx_http_image \n"); 526 | ngx_str_t *value; 527 | value = cf->args->elts; 528 | if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) 529 | { 530 | ngx_http_core_loc_conf_t *clcf; 531 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 532 | clcf->handler = ngx_http_image_handler; 533 | return NGX_CONF_OK; 534 | } 535 | else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) 536 | { 537 | return NGX_CONF_OK; 538 | } 539 | else 540 | { 541 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,"invalid value \"%s\" in \"%s\" directive, ""it must be \"on\" or \"off\"",value[1].data, cmd->name.data); 542 | return NGX_CONF_ERROR; 543 | } 544 | return NGX_CONF_OK; 545 | } 546 | 547 | static ngx_int_t output(ngx_http_request_t *r,void *conf,ngx_str_t type) 548 | { 549 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, "FUNC output \n"); 550 | ngx_int_t status = 0; 551 | ngx_image_conf_t *info = conf; 552 | ngx_http_complex_value_t cv; 553 | 554 | ngx_pool_cleanup_t *cln; 555 | cln = ngx_pool_cleanup_add(r->pool, 0); 556 | if (cln == NULL) { 557 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, "FUNC output 1\n"); 558 | gd_clean_data(info); 559 | return status; 560 | } 561 | 562 | PRINT_LOG(NGX_LOG_ERR, r->connection->log, "FUNC output 2\n"); 563 | cln->handler = gd_clean_data; 564 | cln->data = info; 565 | ngx_memzero(&cv, sizeof(ngx_http_complex_value_t)); 566 | cv.value.len = info->img_size; 567 | cv.value.data = (u_char *)info->img_data; 568 | status = ngx_http_send_response(r, NGX_HTTP_OK, &type, &cv); 569 | return status; 570 | } 571 | 572 | static void thumb_to_string(void *conf) 573 | { 574 | ngx_image_conf_t *info = conf; 575 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC thumb_to_string \n"); 576 | switch(info->dest_type) 577 | { 578 | case NGX_IMAGE_PNG: 579 | info->img_data = gdImagePngPtr(info->dst_im,&info->img_size); 580 | break; 581 | case NGX_IMAGE_GIF: 582 | info->img_data = gdImageGifPtr(info->dst_im,&info->img_size); 583 | break; 584 | case NGX_IMAGE_JPEG: 585 | info->img_data = gdImageJpegPtr(info->dst_im,&info->img_size,info->jpeg_quality); 586 | break; 587 | } 588 | 589 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 590 | "FUNC thumb_to_string, gdImageDestroy(info->dst_im), info->dst_im = %p\n", info->dst_im); 591 | if (info->dst_im) 592 | { 593 | gdImageDestroy(info->dst_im); 594 | info->dst_im = NULL; 595 | } 596 | } 597 | 598 | static void gd_clean_data(void *data){ 599 | ngx_image_conf_t *info = data; 600 | if (info->img_data) 601 | { 602 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 603 | "FUNC gd_clean_data, gdFree(info->img_data), info->img_data = %p, info->img_size = %d \n", 604 | info->img_data, info->img_size); 605 | gdFree(info->img_data); 606 | info->img_data = NULL; 607 | } 608 | 609 | if (info->src_im) 610 | { 611 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 612 | "FUNC gd_clean_data, gdImageDestroy(info->src_im), info->src_im = %p \n", info->src_im); 613 | gdImageDestroy(info->src_im); 614 | info->src_im = NULL; 615 | } 616 | 617 | if (info->dst_im) 618 | { 619 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 620 | "FUNC gd_clean_data, gdImageDestroy(info->dst_im), info->dst_im = %p \n", info->dst_im); 621 | gdImageDestroy(info->dst_im); 622 | info->dst_im = NULL; 623 | } 624 | 625 | if (info->w_im) 626 | { 627 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 628 | "FUNC gd_clean_data, gdImageDestroy(info->w_im), info->w_im = %p \n", info->w_im); 629 | gdImageDestroy(info->w_im); 630 | info->w_im = NULL; 631 | } 632 | 633 | if (info->water_im) 634 | { 635 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 636 | "FUNC gd_clean_data, gdImageDestroy(info->water_im), info->water_im = %p \n", info->water_im); 637 | gdImageDestroy(info->water_im); 638 | info->water_im = NULL; 639 | } 640 | } 641 | 642 | static void make_thumb(void *conf) 643 | { 644 | int colors = 0; 645 | int transparent = -1; 646 | ngx_image_conf_t *info = conf; 647 | info->dst_im = gdImageCreateTrueColor(info->width,info->height); //这里根据转换后的参数生产图片 648 | colors = gdImageColorsTotal(info->src_im); 649 | transparent = gdImageGetTransparent(info->src_im); 650 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC make_thumb \n"); 651 | if (transparent == -1) 652 | { 653 | gdImageSaveAlpha(info->src_im,1); 654 | gdImageColorTransparent(info->src_im, -1); 655 | if(colors == 0) 656 | { 657 | gdImageAlphaBlending(info->dst_im,0); 658 | gdImageSaveAlpha(info->dst_im,1); 659 | } 660 | if(colors) 661 | { 662 | gdImageTrueColorToPalette(info->dst_im,1,256); 663 | } 664 | } 665 | if(info->w_margin == 1) 666 | { 667 | 668 | info->w_im = gdImageCreateTrueColor(info->width,info->height); 669 | gdImageFilledRectangle(info->w_im, 0, 0, info->width,info->height, gdImageColorAllocate(info->w_im, 255, 255, 255)); 670 | if (info->dst_im) 671 | { 672 | gdImageDestroy(info->dst_im); 673 | info->dst_im = NULL; 674 | } 675 | info->dst_im = gdImageCreateTrueColor(info->max_width,info->max_height); 676 | gdImageFilledRectangle(info->dst_im, 0, 0, info->max_width,info->max_height, gdImageColorAllocate(info->dst_im, 255, 255, 255)); 677 | gdImageCopyResampled(info->w_im, info->src_im, 0, 0, info->src_x, info->src_y,info->width, info->height, info->src_w,info->src_h); 678 | gdImageCopyResampled(info->dst_im, info->w_im, info->dst_x,info->dst_y, 0, 0,info->width, info->height, info->width, info->height); 679 | gdImageDestroy(info->w_im); 680 | info->w_im = NULL; 681 | } 682 | else 683 | { 684 | gdImageCopyResampled(info->dst_im,info->src_im,info->dst_x,info->dst_y,info->src_x,info->src_y,info->width,info->height,info->src_w,info->src_h); 685 | } 686 | 687 | gdImageDestroy(info->src_im); 688 | info->src_im = NULL; 689 | } 690 | static void water_mark(void *conf) 691 | { 692 | ngx_image_conf_t *info = conf; 693 | int water_w=0;//水印宽度 694 | int water_h=0;//水印高度 695 | int posX = 0;//X位置 696 | int posY = 0;//Y位置 697 | int water_color = 0;//文字水印GD颜色值 698 | char *water_text;//图片文字 699 | char *water_font;//文字字体 700 | char *water_color_text;//图片颜色值 701 | water_text = NULL; 702 | water_font = NULL; 703 | water_color_text = NULL; 704 | 705 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC water_mark \n"); 706 | if(info->water_status)//如果水印功能打开了 707 | { 708 | 709 | if(info->water_type == 0)//如果为图片水印 710 | { 711 | if(file_exists((char *)info->water_image.data) == 0)//判断水印图片是否存在 712 | { 713 | water_image_from(conf);//获取水印图片信息 714 | if(info->water_im == NULL)//判断对象是否为空 715 | { 716 | return;//水印文件异常 717 | } else { 718 | water_w = info->water_im->sx; 719 | water_h = info->water_im->sy; 720 | } 721 | } 722 | else 723 | { 724 | return;//水印图片不存在 725 | } 726 | } 727 | else//文字水印 728 | { 729 | water_text = (char *) info->water_text.data; 730 | water_color_text = (char *) info->water_color.data; 731 | water_font = (char *)info->water_font.data; 732 | if(file_exists((char *)water_font) == 0)//如果水印字体存在 733 | { 734 | int R,G,B; 735 | char R_str[3],G_str[3],B_str[3]; 736 | int brect[8]; 737 | gdImagePtr font_im; 738 | font_im = gdImageCreateTrueColor(info->dst_im->sx,info->dst_im->sy); 739 | sprintf(R_str,"%.*s",2,water_color_text+1); 740 | sprintf(G_str,"%.*s",2,water_color_text+3); 741 | sprintf(B_str,"%.*s",2,water_color_text+5); 742 | sscanf(R_str,"%x",&R); 743 | sscanf(G_str,"%x",&G); 744 | sscanf(B_str,"%x",&B); 745 | water_color = gdImageColorAllocate(info->dst_im,R,G,B); 746 | gdImageStringFT(font_im, &brect[0], water_color, water_font, info->water_font_size, 0.0, 0, 0,water_text/*, &strex*/); 747 | //water_w = abs(brect[2] - brect[6] + 10); 748 | water_w = abs(brect[2] - brect[6] + 10); 749 | water_h = abs(brect[3] - brect[7]); 750 | gdImageDestroy(font_im); 751 | } 752 | 753 | } 754 | if( (info->width < info->water_width_min) || info->height < info->water_height_min) 755 | { 756 | return;//如果图片宽度/高度比配置文件里规定的宽度/高度宽度小 757 | } 758 | if ((info->width < water_w) || (info->height < water_h)) 759 | { 760 | return;//如果图片宽度/高度比水印宽度/高度宽度小 761 | } 762 | if(info->water_pos < 1 ||info->water_pos > 9) 763 | { 764 | srand((unsigned)time(NULL)); 765 | //info->water_pos = rand() % 9 + 1; 766 | info->water_pos = 1+(int)(9.0*rand()/(RAND_MAX+1.0)); 767 | //info->water_pos = rand() % 9; 768 | } 769 | switch(info->water_pos) 770 | { 771 | case 1: 772 | posX = 10; 773 | posY = 15; 774 | break; 775 | case 2: 776 | posX = (info->width - water_w) / 2; 777 | posY = 15; 778 | break; 779 | case 3: 780 | posX = info->width - water_w; 781 | posY = 15; 782 | break; 783 | case 4: 784 | posX = 0; 785 | posY = (info->height - water_h) / 2; 786 | break; 787 | case 5: 788 | posX = (info->width - water_w) / 2; 789 | posY = (info->height - water_h) / 2; 790 | break; 791 | case 6: 792 | posX = info->width - water_w; 793 | posY = (info->height - water_h) / 2; 794 | break; 795 | case 7: 796 | posX = 0; 797 | posY = (info->height - water_h); 798 | break; 799 | case 8: 800 | posX = (info->width - water_w) /2; 801 | posY = info->width - water_h; 802 | break; 803 | case 9: 804 | posX = info->width - water_w; 805 | posY = info->height - water_h; 806 | break; 807 | default: 808 | posX = info->width - water_w; 809 | posY = info->height - water_h; 810 | break; 811 | } 812 | if(info->water_type == 0) 813 | { 814 | gdImagePtr tmp_im; 815 | tmp_im = NULL; 816 | tmp_im = gdImageCreateTrueColor(water_w, water_h); 817 | gdImageCopy(tmp_im, info->dst_im, 0, 0, posX, posY, water_w, water_h); 818 | gdImageCopy(tmp_im, info->water_im, 0, 0, 0, 0, water_w, water_h); 819 | gdImageCopyMerge(info->dst_im, tmp_im,posX, posY, 0, 0, water_w,water_h,info->water_transparent); 820 | gdImageDestroy(tmp_im); 821 | gdImageDestroy(info->water_im); 822 | info->water_im = NULL; 823 | } 824 | else 825 | { 826 | gdImageAlphaBlending(info->dst_im,-1); 827 | gdImageSaveAlpha(info->dst_im,0); 828 | gdImageStringFT(info->dst_im,0,water_color,water_font,info->water_font_size, 0.0, posX, posY,water_text); 829 | } 830 | } 831 | } 832 | 833 | static int parse_image_info(void *conf) 834 | { 835 | void *(*old_pcre_malloc)(size_t); 836 | void (*old_pcre_free)(void *); 837 | pcre *expr;//正则 838 | char *pattern; 839 | const char *error;//正则错误内容 840 | int pcre_state=0;//匹配图片规则状态,0为成功 -1为失败 841 | int erroffset;//正则错误位置 842 | int ovector[30];//识别器读取原图图片到GD对象 843 | int expr_res;//正则匹配指针 844 | int i=0;//循环用 845 | ngx_image_conf_t *info = conf; 846 | info->request_filename = NULL; 847 | old_pcre_malloc = pcre_malloc; 848 | old_pcre_free = pcre_free; 849 | pcre_malloc = malloc; 850 | pcre_free = free; 851 | 852 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC parse_image_info \n"); 853 | if(strchr(info->dest_file,'!')) 854 | { 855 | info->pcre_type = 0; 856 | //pattern = "([^<]*)!([a-z])(\\d{2,4})x(\\d{2,4}).([a-zA-Z]{3,4})";//正则表达式 857 | pattern = "([^<]*)\\/([^<]*)!([a-z])(\\d{2,4})x(\\d{2,4}).([a-zA-Z]{3,4})";//正则表达式 858 | } 859 | else 860 | { 861 | info->pcre_type = 1; 862 | //pattern = "([^<]*).([a-z])(\\d{2,4})x(\\d{2,4}).([a-zA-Z]{3,4})";//正则表达式 863 | pattern = "([^<]*)\\/([^<]*).([a-z])(\\d{2,4})x(\\d{2,4}).([a-zA-Z]{3,4})";//正则表达式 864 | } 865 | expr = pcre_compile((const char *)pattern,0,&error,&erroffset,NULL); 866 | if(expr != NULL) 867 | { 868 | expr_res = pcre_exec(expr,NULL,(const char *)info->dest_file,ngx_strlen(info->dest_file),0,0,ovector,30); 869 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 870 | "FUNC parse_image_info, expr_res=%d\n", expr_res); 871 | 872 | //printf("parse_image_info dest_file=%s,%ld\n", (const char *)info->dest_file, ngx_strlen(info->dest_file)); 873 | if (expr_res > 5) 874 | { 875 | for(i=0; idest_file + ovector[2*i]; 878 | int substring_length = ovector[2*i+1] - ovector[2*i]; 879 | sprintf(info->buffer[i],"%.*s",substring_length,substring_start); 880 | //printf("parse_image_info buff[%d] : %d %s\n",i,substring_length,substring_start); 881 | } 882 | 883 | info->source_file = info->buffer[1]; 884 | if(info->pcre_type == 1) 885 | { 886 | /** combind source_file **/ 887 | strcat(info->source_file,"/"); 888 | strcat(info->source_file, info->buffer[2]); 889 | strcat(info->source_file,"."); 890 | strcat(info->source_file, info->buffer[expr_res-1]); 891 | /** combind request_filename **/ 892 | info->request_filename = info->buffer[2]; 893 | strcat(info->request_filename,"."); 894 | strcat(info->request_filename, info->buffer[expr_res-1]); 895 | } 896 | else 897 | { 898 | /** combind source_file **/ 899 | strcat(info->source_file,"/"); 900 | strcat(info->source_file,info->buffer[2]); 901 | /** combind request_filename **/ 902 | info->request_filename = info->buffer[2]; 903 | } 904 | 905 | dirname(info->buffer[1],info->local_dir); 906 | info->dest_file = info->buffer[0]; 907 | info->m_type = info->buffer[3]; 908 | info->max_width = atoi(info->buffer[4]); //超大时会出错 909 | info->max_height = atoi(info->buffer[5]); 910 | info->max_width = (info->max_width > 2000) ? 2000 : (info->max_width); //限定最大值 911 | info->max_height = (info->max_height > 2000) ? 2000 : info->max_height; 912 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 913 | "FUNC parse_image_info, info->dest_file= %s, info->local_dir= %s, info->source_file = %s, " 914 | "info->m_type = %s, info->max_width = %d, info->max_height = %d, info->request_filename = %s \n", 915 | info->dest_file, info->local_dir, info->source_file, 916 | info->m_type, info->max_width, info->max_height, info->request_filename); 917 | if((info->max_width < 0) || (info->max_height < 0)) 918 | { 919 | //如果图片小于0,则可以判断请求无效了 920 | pcre_free(expr); 921 | pcre_malloc = old_pcre_malloc; 922 | pcre_free = old_pcre_free; 923 | return -1; 924 | } 925 | if(file_exists(info->source_file) == -1)//原图不存在 926 | { 927 | download(conf); 928 | } 929 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 930 | "FUNC parse_image_info, info->dest_file= %s, info->local_dir= %s, info->source_file = %s \n", 931 | info->dest_file, info->local_dir, info->source_file); 932 | if(file_exists(info->source_file) == 0) 933 | { 934 | pcre_state = calc_image_info(conf); 935 | pcre_free(expr); 936 | pcre_malloc = old_pcre_malloc; 937 | pcre_free = old_pcre_free; 938 | return pcre_state; 939 | } 940 | } 941 | 942 | pcre_free(expr); 943 | //恢复Nginx默认PCRE内存分配 944 | pcre_malloc = old_pcre_malloc; 945 | pcre_free = old_pcre_free; 946 | //END 947 | } 948 | 949 | return -1; 950 | } 951 | static int calc_image_info(void *conf) 952 | { 953 | ngx_image_conf_t *info = conf; 954 | info->src_type = get_ext_header(info->source_file);//读取原图头部信息金星判断图片格式 955 | if( info->src_type > 0) 956 | { 957 | info->w_margin = 0;//设置默认图片不补白边 958 | info->src_im = NULL; 959 | info->dst_im = NULL; 960 | info->w_im = NULL; 961 | image_from(conf);//读取原图图片到GD对象 962 | if(info->src_im != NULL) 963 | { 964 | 965 | info->src_width = info->src_im->sx; 966 | info->src_height = info->src_im->sy; 967 | info->src_x = 0; 968 | info->src_y = 0; 969 | info->dst_x = 0; 970 | info->dst_y = 0; 971 | info->src_w = info->src_width; 972 | info->src_h = info->src_height; 973 | info->width = info->max_width; 974 | info->height = info->max_height; 975 | if(stricmp(info->m_type,"c") == 0 || stricmp(info->m_type,"m") == 0) 976 | { 977 | if((double)info->src_width/info->src_height > (double)info->max_width / info->max_height) 978 | { 979 | info->src_w=info->width * info->src_height / info->height; 980 | if(stricmp(info->m_type,"m") == 0) 981 | { 982 | info->src_x=(info->src_width-info->src_w)/2; 983 | } 984 | else 985 | { 986 | info->src_x=(info->src_width-info->src_w)*0.1; 987 | } 988 | } 989 | else 990 | { 991 | info->src_h=info->src_w * info->height / info->width; 992 | if(stricmp(info->m_type,"m") == 0) 993 | { 994 | info->src_y=(info->src_height-info->src_h)/2; 995 | } 996 | else 997 | { 998 | info->src_y=(info->src_height-info->src_h)*0.1; 999 | } 1000 | } 1001 | } 1002 | else if(stricmp(info->m_type,"t") == 0) 1003 | { 1004 | if( (info->max_width >= info->src_width) || (info->max_height >= info->src_height )) 1005 | { 1006 | info->max_width = info->src_width; 1007 | info->max_height = info->src_height; 1008 | info->height = info->src_height; 1009 | info->width = info->src_width; 1010 | } 1011 | else 1012 | { 1013 | if((double)info->src_width/info->src_height >= (double) info->max_width / info->max_height) 1014 | { 1015 | info->height=info->width * info->src_height/info->src_width; 1016 | info->src_h=info->src_w * info->height / info->width; 1017 | info->src_y=(info->src_height - info->src_h) / 2; 1018 | } 1019 | else 1020 | { 1021 | info->width=info->max_height * info->src_width / info->src_height; 1022 | info->src_w=info->width * info->src_height / info->height; 1023 | info->src_x=(info->src_width - info->src_w)/2; 1024 | } 1025 | } 1026 | } 1027 | else if(stricmp(info->m_type,"w") == 0) 1028 | { 1029 | info->w_margin = 1; 1030 | if((double)info->src_width/info->src_height >= (double) info->max_width / info->max_height) 1031 | { 1032 | info->height=info->width * info->src_height/info->src_width; 1033 | info->src_h=info->src_w * info->height / info->width; 1034 | info->src_y=(info->src_height - info->src_h) / 2; 1035 | } 1036 | else 1037 | { 1038 | info->width=info->max_height * info->src_width / info->src_height; 1039 | info->src_w=info->width * info->src_height / info->height; 1040 | info->src_x=(info->src_width - info->src_w)/2; 1041 | } 1042 | info->dst_x = (float)((float)(info->max_width - info->width)/2); 1043 | info->dst_y = (float)((float)(info->max_height - info->height)/2); 1044 | 1045 | } 1046 | else 1047 | { 1048 | gdImageDestroy(info->src_im); 1049 | info->src_im = NULL; 1050 | return -1; 1051 | } 1052 | 1053 | return 0; 1054 | } 1055 | } 1056 | 1057 | gdImageDestroy(info->src_im); 1058 | info->src_im = NULL; 1059 | return -1; 1060 | } 1061 | 1062 | static void check_image_type(void *conf) 1063 | { 1064 | ngx_image_conf_t *info = conf; 1065 | info->extension = get_ext(info->dest_file); 1066 | if(stricmp(info->extension,"jpg") == 0 || stricmp(info->extension,"jpeg") == 0 || stricmp(info->extension,"jpe") == 0) 1067 | { 1068 | info->dest_type = NGX_IMAGE_JPEG; 1069 | } 1070 | else if(stricmp(info->extension,"png") == 0) 1071 | { 1072 | info->dest_type = NGX_IMAGE_PNG; 1073 | } 1074 | else if(stricmp(info->extension,"gif") == 0) 1075 | { 1076 | info->dest_type = NGX_IMAGE_GIF; 1077 | } 1078 | else 1079 | { 1080 | 1081 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "ERROR 不支持的扩展名=%s", info->extension); 1082 | info->dest_type = NGX_IMAGE_NONE; 1083 | } 1084 | } 1085 | 1086 | static char * get_ext(char *filename) 1087 | { 1088 | char *p = strrchr(filename, '.'); 1089 | if(p != NULL) 1090 | { 1091 | return p+1; 1092 | } 1093 | else 1094 | { 1095 | return filename; 1096 | } 1097 | } 1098 | 1099 | static int file_exists(char *filename) 1100 | { 1101 | if(access(filename, 0) != -1) 1102 | { 1103 | return 0; 1104 | } 1105 | else 1106 | { 1107 | return -1; 1108 | } 1109 | } 1110 | 1111 | static int get_ext_header(char *filename) 1112 | { 1113 | FILE *handle; 1114 | handle = fopen(filename, "rb"); 1115 | if(!handle) 1116 | { 1117 | return NGX_IMAGE_NONE; 1118 | } 1119 | else 1120 | { 1121 | unsigned short filetype;//bmp 0x4D42 1122 | if(fread(&filetype,sizeof(unsigned short),1,handle) != 1) 1123 | { 1124 | fclose(handle); 1125 | return NGX_IMAGE_NONE; 1126 | } 1127 | fclose(handle); 1128 | if( filetype == 0xD8FF) 1129 | { 1130 | return NGX_IMAGE_JPEG; 1131 | } 1132 | else if( filetype == 0x4947) 1133 | { 1134 | return NGX_IMAGE_GIF; 1135 | } 1136 | else if( filetype == 0x5089) 1137 | { 1138 | return NGX_IMAGE_PNG; 1139 | } 1140 | else 1141 | { 1142 | return NGX_IMAGE_NONE; 1143 | } 1144 | } 1145 | return NGX_IMAGE_NONE; 1146 | } 1147 | 1148 | static void write_img(void * conf) 1149 | { 1150 | ngx_image_conf_t *info = conf; 1151 | FILE * fp; 1152 | 1153 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC write_img \n"); 1154 | if(info->img_data == NULL) 1155 | { 1156 | return; 1157 | } 1158 | 1159 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 1160 | "FUNC write_img, info->dest_file = %s, info->img_data = %p, info->img_size = %d \n", 1161 | info->dest_file, info->img_data, info->img_size); 1162 | fp = fopen(info->dest_file,"wb"); 1163 | if(fp) 1164 | { 1165 | fwrite(info->img_data,sizeof(char),info->img_size,fp); 1166 | fclose(fp); 1167 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 1168 | "FUNC write_img, info->img_data = %p, info->img_size = %d \n", info->img_data, info->img_size); 1169 | } 1170 | 1171 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 1172 | "FUNC write_img, gdFree(info->img_data), info->img_data = %p \n", info->img_data); 1173 | gdFree(info->img_data); 1174 | info->img_data = NULL; 1175 | } 1176 | 1177 | static int read_img(char **filename,int * size,void ** buffer, ngx_http_request_t *request) 1178 | { 1179 | PRINT_LOG(NGX_LOG_ERR, request->connection->log, "FUNC read_img, filename = %s \n", *filename); 1180 | FILE *fp; 1181 | struct stat stat_buffer; 1182 | 1183 | fp = fopen(*filename, "rb"); 1184 | if (fp) 1185 | { 1186 | fstat(fileno(fp),&stat_buffer); 1187 | if (stat_buffer.st_size <= 0) 1188 | { 1189 | return -1; 1190 | } 1191 | 1192 | //*buffer = malloc(stat_buffer.st_size); 1193 | if (NULL == request) { 1194 | return -1; 1195 | } 1196 | 1197 | *buffer = ngx_palloc(request->pool, stat_buffer.st_size); 1198 | if (NULL == *buffer) { 1199 | ngx_pfree(request->pool, *buffer); 1200 | return -1; 1201 | } 1202 | 1203 | if(fread(*buffer,1,stat_buffer.st_size,fp)) 1204 | { 1205 | *size = stat_buffer.st_size; 1206 | PRINT_LOG(NGX_LOG_ERR, request->connection->log, 1207 | "FUNC read_img, ngx_palloc, *buffer = %p, size = %d \n", *buffer, stat_buffer.st_size); 1208 | } 1209 | 1210 | fclose(fp); 1211 | return 0; 1212 | } 1213 | return -1; 1214 | } 1215 | 1216 | static void water_image_from(void * conf) 1217 | { 1218 | int size = 0; 1219 | void * buffer; 1220 | char * water_file; 1221 | 1222 | ngx_image_conf_t *info = conf; 1223 | info->water_im = NULL; 1224 | water_file = (char *)info->water_image.data; 1225 | info->water_im_type = get_ext_header(water_file); 1226 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC water_image_from \n"); 1227 | if((read_img(&water_file,&size,&buffer, info->request)) == 0) 1228 | { 1229 | switch(info->water_im_type) 1230 | { 1231 | case NGX_IMAGE_GIF: 1232 | info->water_im = gdImageCreateFromGifPtr(size,buffer); 1233 | break; 1234 | case NGX_IMAGE_JPEG: 1235 | info->water_im = gdImageCreateFromJpegPtr(size,buffer); 1236 | break; 1237 | case NGX_IMAGE_PNG: 1238 | info->water_im = gdImageCreateFromPngPtr(size,buffer); 1239 | break; 1240 | } 1241 | 1242 | //free(buffer); 1243 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 1244 | "FUNC water_image_from, ngx_pfree, buffer = %p\n", buffer); 1245 | ngx_pfree(info->request->pool, buffer); 1246 | return; 1247 | } 1248 | } 1249 | 1250 | static void image_from(void * conf) 1251 | { 1252 | int size = 0; 1253 | void * buffer; 1254 | ngx_image_conf_t *info = conf; 1255 | if (info->src_im) 1256 | { 1257 | gdImageDestroy(info->src_im); 1258 | } 1259 | 1260 | info->src_im = NULL; 1261 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC image_from \n"); 1262 | if((read_img(&info->source_file,&size,&buffer, info->request)) == 0) 1263 | { 1264 | switch(info->src_type) 1265 | { 1266 | case NGX_IMAGE_GIF: 1267 | info->src_im = gdImageCreateFromGifPtr(size,buffer); 1268 | break; 1269 | case NGX_IMAGE_JPEG: 1270 | info->src_im = gdImageCreateFromJpegPtr(size,buffer); 1271 | break; 1272 | case NGX_IMAGE_PNG: 1273 | info->src_im = gdImageCreateFromPngPtr(size,buffer); 1274 | break; 1275 | } 1276 | 1277 | //free(buffer); 1278 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, 1279 | "FUNC image_from, ngx_pfree, buffer = %p\n", buffer); 1280 | ngx_pfree(info->request->pool, buffer); 1281 | return; 1282 | } 1283 | } 1284 | 1285 | static int get_header(char *url) 1286 | { 1287 | long result = 0; 1288 | CURL *curl; 1289 | CURLcode status; 1290 | 1291 | curl = curl_easy_init(); 1292 | 1293 | memset(&status,0,sizeof(CURLcode)); 1294 | if(curl) 1295 | { 1296 | curl_easy_setopt(curl, CURLOPT_URL,url); 1297 | curl_easy_setopt(curl, CURLOPT_NOBODY, 1); 1298 | curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST,"GET"); 1299 | status = curl_easy_perform(curl); 1300 | if(status != CURLE_OK) 1301 | { 1302 | curl_easy_cleanup(curl); 1303 | return -1; 1304 | } 1305 | status = curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&result); 1306 | if((status == CURLE_OK) && result == 200) 1307 | { 1308 | curl_easy_cleanup(curl); 1309 | return 0; 1310 | } 1311 | curl_easy_cleanup(curl); 1312 | return -1; 1313 | } 1314 | return -1; 1315 | } 1316 | 1317 | static size_t curl_get_data(void *ptr, size_t size, size_t nmemb, void *stream) 1318 | { 1319 | int written = fwrite (ptr, size, nmemb, (FILE *) curl_handle); 1320 | return written; 1321 | } 1322 | 1323 | static void dirname(char *path,char *dirpath) 1324 | { 1325 | char dirname[MAX_DIR_PATH_LEN]; 1326 | int len = 0; 1327 | len=strlen(path); 1328 | memset(dirname,0,sizeof(dirname)); 1329 | for (;len>0;len--) 1330 | { 1331 | //从最后一个元素开始找.直到找到第一个'/' 1332 | if(path[len]=='/') 1333 | { 1334 | strncpy(dirname,path,len+1); 1335 | dirname[len] = '\0'; 1336 | break; 1337 | } 1338 | } 1339 | 1340 | if (dirpath) { 1341 | strcpy(dirpath, dirname); 1342 | } 1343 | } 1344 | 1345 | static int create_dir(char *dir) 1346 | { 1347 | int i; 1348 | int len; 1349 | char dirname[MAX_DIR_PATH_LEN]; 1350 | strcpy(dirname,dir); 1351 | len=strlen(dirname); 1352 | if(dirname[len-1]!='/') 1353 | { 1354 | strcat(dirname,"/"); 1355 | } 1356 | len=strlen(dirname); 1357 | for(i=1; ibackend_server.data); 1380 | strcat(real_url,info->request_dir); 1381 | strcat(real_url,"/"); 1382 | strcat(real_url, info->request_filename); 1383 | info->request_source = strdup(real_url); 1384 | } 1385 | 1386 | 1387 | static void download(void * conf) 1388 | { 1389 | ngx_image_conf_t *info = conf; 1390 | 1391 | PRINT_LOG(NGX_LOG_ERR, info->request->connection->log, "FUNC download \n"); 1392 | get_request_source(conf);//取得请求的URL的原始文件 1393 | if (get_header(info->request_source) == 0) 1394 | { 1395 | CURL *curl; 1396 | curl = curl_easy_init(); 1397 | create_dir(info->local_dir);//创建目录 1398 | if((curl_handle = fopen(info->source_file, "wb")) == NULL) 1399 | { 1400 | curl_easy_cleanup (curl); 1401 | fclose(curl_handle); 1402 | curl_handle = NULL; 1403 | return; 1404 | } 1405 | if(curl) 1406 | { 1407 | curl_easy_setopt(curl, CURLOPT_URL,info->request_source); 1408 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_get_data); 1409 | curl_easy_perform(curl); 1410 | curl_easy_cleanup(curl); 1411 | fclose(curl_handle); 1412 | curl_handle = NULL; 1413 | } 1414 | } 1415 | free(info->request_source); 1416 | } 1417 | 1418 | 1419 | --------------------------------------------------------------------------------