Chinaunix首页 | 论坛 | 博客
  • 博客访问: 64193
  • 博文数量: 21
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 236
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-05 21:34
文章分类

全部博文(21)

文章存档

2015年(21)

我的朋友

分类: 服务器与存储

2015-08-10 20:12:41

Nginx的HTTP模块下载文件和传送缓冲区的字符串差不多,只需将文件标志置为1即可,我转送的文件是mp3的,所以HTTP的那个mine 类型要写为audio/mp3,二话不说了,贴代码,代码和之前那个helloworld差不多。

点击(此处)折叠或打开

  1. //start from the very beginning,and to create greatness
  2. //@author: Chuangwei Lin
  3. //@E-mail:979951191@qq.com
  4. //@brief:nginx的HTTP模块:发送文件

  5. #include <ngx_config.h>//包含必要的头文件
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>
  8. //先声明函数
  9. static char *ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  10. static ngx_int_t ngx_http_lcwsendfile_handler(ngx_http_request_t *r);


  11. //ngx_command_t定义模块的配置文件参数
  12. static ngx_command_t ngx_http_lcwsendfile_commands[] =
  13. {
  14.     {
  15.      //配置项名称,就是配置文件里面的那个名称
  16.         ngx_string("lcwsendfile"),
  17.         //配置项类型,将指定配置项可以出现的位置
  18.         //例如出现在server{}或location{}中,以及他可以携带的参数个数
  19.          NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
  20.          //ngx_command_t结构体中的set成员,
  21.          //当在某个配置块中出现lcwsendfile配置项时,Nginx将会调用ngx_http_lcwsendfile方法
  22.          //ngx_http_lcwsendfile方法将在下面实现
  23.          ngx_http_lcwsendfile,
  24.          //在配置文件中的偏移量conf
  25.          NGX_HTTP_LOC_CONF_OFFSET,
  26.          //offset通常用于使用预设的解析方法解析配置项,需要与conf配合使用
  27.          0,
  28.          //配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
  29.          NULL
  30.     },
  31.     //ngx_null_command是一个空的ngx_command_t结构,用来表示数组的结尾
  32.     ngx_null_command
  33. };
  34. //ngx_http_module_t的8个回调方法,因为目前没有什么工作是必须在HTTP框架初始化
  35. //时完成的,所以暂时不必实现ngx_http_module_t的8个回调方法
  36. static ngx_http_module_t ngx_http_lcwsendfile_module_ctx =
  37. {
  38.     NULL, // preconfiguration解析配置文件前调用
  39.     NULL, // postconfiguration 完成配置文件解析后调用

  40.     NULL, // create main configuration当需要创建数据结构用于存储main级别的
  41.                 //(直属于http{}块的配置项)的全局配置项时
  42.     NULL, // init main configuration常用于初始化main级别的配置项

  43.     NULL, // create server configuration当需要创建数据结构用于存储srv级别的
  44.                 //(直属于server{}块的配置项)的配置项时
  45.     NULL, // merge server configuration用于合并main级别和srv级别下的同名配置项

  46.     NULL, // create location configuration 当需要创建数据结构用于存储loc级别的
  47.                 //(直属于location{}块的配置项)的配置项时
  48.     NULL // merge location configuration 用于合并srv和loc级别下的同名配置项
  49. };
  50. //定义lcwsendfile模块
  51. //lcwtest模块在编译时会被加入到ngx_modules全局数组中
  52. //Nginx在启动时,会调用所有模块的初始化回调方法
  53. //HTTP框架初始化时会调用ngx_http_module_t中的8个方法
  54. //HTTP模块数据结构
  55. ngx_module_t ngx_http_lcwsendfile_module =
  56. {
  57.     NGX_MODULE_V1,//该宏为下面的ctx_index,index,spare0,spare1,spare2,spare3,version变量
  58.                  //提供了初始化的值:0,0,0,0,0,0,1
  59.     //ctx_index表示当前模块在这类模块中的序号
  60.     //index表示当前模块在所有模块中的序号,Nginx启动时会根据ngx_modules数组设置各模块的index值
  61.     //spare0 spare系列的保留变量,暂未使用
  62.     //spare1
  63.     //spare2
  64.     //spare3
  65.     //version模块的版本,便于将来的扩展,目前只有一种,默认为1
  66.     &ngx_http_lcwsendfile_module_ctx, //ctx用于指向一类模块的上下文结构
  67.     ngx_http_lcwsendfile_commands, //commands将处理nginx.conf中的配置项
  68.     NGX_HTTP_MODULE, //模块的类型,与ctx指针紧密相关,取值范围是以下5种:
  69.                             //NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE
  70.     //以下7个函数指针表示有7个执行点会分别调用这7种方法,对于任一个方法而言,如果不需要nginx在某个是可执行它
  71.     //那么简单地将他设为空指针即可
  72.     NULL, //master进程启动时回调init_master
  73.     NULL, //init_module回调方法在初始化所有模块时被调用,在master/worker模式下,
  74.                                     //这个阶段将在启动worker子进程前完成
  75.     NULL, //init_process回调方法在正常服务前被调用,在master/worker模式下,
  76.                                     //多个worker子进程已经产生,在每个worker子进程的初始化过程会调用所有模块的init_process函数
  77.     NULL, //由于nginx暂不支持多线程模式,所以init thread在框架代码中没有被调用过
  78.     NULL, // exit thread,也不支持
  79.     NULL, //exit process回调方法将在服务停止前调用,在master/worker模式下,worker进程会在退出前调用它
  80.     NULL, //exit master回调方法将在master进程退出前被调用
  81.     NGX_MODULE_V1_PADDING //这里是8个spare_hook变量,是保留字段,目前没有使用,Nginx提供了NGX_MODULE_V1_PADDING宏来填充
  82. };

  83. /******************************************************
  84. 函数名:ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  85. 参数:
  86. 功能:lcwsendfile方法的实现
  87. *******************************************************/
  88. static char* ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  89. {
  90.     ngx_http_core_loc_conf_t *clcf;
  91.     //首先找到lcwtest配置项所属的配置块,clcf貌似是location块内的数据
  92.     //结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个
  93.     //http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体
  94.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  95.     //http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果
  96.     //请求的主机域名、URI与lcwsendfile配置项所在的配置块相匹配,就将调用我们
  97.     //实现的ngx_http_lcwsendfile_handler方法处理这个请求
  98.     //ngx_http_lcwsendfile_handler将在下面实现
  99.     clcf->handler = ngx_http_lcwsendfile_handler;
  100.     return NGX_CONF_OK;
  101. }
  102. /******************************************************
  103. 函数名:ngx_http_lcwsendfile_handler(ngx_http_request_t *r)
  104. 参数:ngx_http_request_t结构体
  105. 功能:ngx_http_lcwtest_handler方法的实现
  106. *******************************************************/
  107. static ngx_int_t ngx_http_lcwsendfile_handler(ngx_http_request_t *r)
  108. {
  109.     //必须是GET或者HEAD方法,否则返回405 Not Allowed
  110.     if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
  111.     {
  112.         return NGX_HTTP_NOT_ALLOWED;
  113.     }
  114.     //丢弃请求中的包体
  115.     ngx_int_t rc = ngx_http_discard_request_body(r);
  116.     if (rc != NGX_OK)
  117.     {
  118.         return rc;
  119.     }
  120.     //构造ngx_buf_t结构准备发送包体
  121.     ngx_buf_t *b;
  122.     //开辟ngx_buf_t缓冲区
  123.     b = ngx_palloc(r->pool, sizeof(ngx_buf_t));
  124.     //要打开的文件
  125.     u_char* filename = (u_char*)"/mnt/hgfs/lcw_program/张杰 - My Sunshine.mp3";
  126.     b->in_file = 1;//将in_file设置为1就表示ngx_buf_t缓冲区传送的是文件而不是内存
  127.     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
  128.     //打开文件传送文件描述符
  129.     b->file->fd = ngx_open_file(filename, NGX_FILE_RDONLY | NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
  130.     b->file->log = r->connection->log;
  131.     b->file->name.data = filename;
  132.     b->file->name.len = sizeof(filename) - 1;
  133.     if (b->file->fd <= 0)//打开失败
  134.     {
  135.         return NGX_HTTP_NOT_FOUND;
  136.     }

  137.     //支持断点续传
  138.     r->allow_ranges = 1;

  139.     //获取文件长度ngx_file_info是linux系统中stat的一个宏
  140.     if (ngx_file_info(filename, &b->file->info) == NGX_FILE_ERROR)
  141.     {
  142.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  143.     }

  144.     //设置缓冲区指向的文件块,这里是告诉Nginx从文件的file_pos偏移量开始发送文件
  145.     //一直到达file_last偏移处截止
  146.     b->file_pos = 0;
  147.     b->file_last = b->file->info.st_size;
  148.     //接下来要关闭文件句柄,否则会出现句柄泄露问题,于是定义如下结构体
  149.     //ngx_pool_cleanup_add用于告诉HTTP框架,在请求结束时调用cln的handler方法清理资源
  150.     ngx_pool_cleanup_t* cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
  151.     if (cln == NULL)
  152.     {
  153.         return NGX_ERROR;
  154.     }
  155.     //赋值执行实际清理资源工作的回调方法,ngx_pool_cleanup_file方法的作用是把文件句柄关闭
  156.     //ngx_pool_cleanup_file方法需要一个ngx_pool_cleanup_file_t类型的参数
  157.     cln->handler = ngx_pool_cleanup_file;
  158.     ngx_pool_cleanup_file_t *clnf = cln->data;
  159.     clnf->fd = b->file->fd;//文件句柄
  160.     clnf->name = b->file->name.data;//文件名称
  161.     clnf->log = r->pool->log;//日志对象
  162.     //奇怪?这里怎么把ngx_pool_cleanup_file_t参数传给ngx_pool_cleanup_file方法?
  163.     //设置返回的Content-Type。注意,ngx_str_t有一个很方便的初始化宏
  164.     //ngx_string,它可以把ngx_str_t的data和len成员都设置好
  165.     ngx_str_t type = ngx_string("audio/mp3");
  166.     //设置返回状态码
  167.     r->headers_out.status = NGX_HTTP_OK;
  168.     //响应包是有包体内容的,所以需要设置Content-Length长度
  169.     r->headers_out.content_length_n = b->file->info.st_size;
  170.     //设置Content-Type
  171.     r->headers_out.content_type = type;
  172.     //发送http头部
  173.     rc = ngx_http_send_header(r);
  174.     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
  175.     {
  176.         return rc;
  177.     }
  178.     //构造发送时的ngx_chain_t结构体
  179.     ngx_chain_t out;
  180.     //赋值ngx_buf_t
  181.     out.buf = b;
  182.     //设置next为NULL
  183.     out.next = NULL;
  184.     //最后一步发送包体,http框架会调用ngx_http_finalize_request方法
  185.     //结束请求
  186.     return ngx_http_output_filter(r, &out);
  187. }
配置文件也是差不多,编译方法也一样。不过我总是遇到一个问题,就是在windows新建的config文件有时候好像被识别不了,最后我在linux下写这个文件就可以了有点奇怪
还有编译之后把模块加进nginx里,然后好像上次的那个就没有了,难道以后都得全部加进去吗?
运行可以得到下面的结果:
                   
下载成功,
                                      
是张杰的MySunshine,文件没有损坏,可以听
阅读(1869) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

sandynie2016-01-05 20:23:33

b->file->name.data = filename;
  b->file->name.len = sizeof(filename) - 1; 
 这里的sizeof一直是 指针的长度。strlen(filename)才是字符串的长度。求楼主解答