zoukankan      html  css  js  c++  java
  • Nginx模块开发(2)————下载文件

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

    //start from the very beginning,and to create greatness
    //@author: Chuangwei Lin
    //@E-mail:979951191@qq.com
    //@brief:nginx的HTTP模块:发送文件
    
    #include <ngx_config.h>//包含必要的头文件
    #include <ngx_core.h>
    #include <ngx_http.h>
    //先声明函数
    static char *ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    static ngx_int_t ngx_http_lcwsendfile_handler(ngx_http_request_t *r);
    
    
    //ngx_command_t定义模块的配置文件参数
    static ngx_command_t ngx_http_lcwsendfile_commands[] =
    {
        {
         //配置项名称,就是配置文件里面的那个名称
            ngx_string("lcwsendfile"),
            //配置项类型,将指定配置项可以出现的位置
            //例如出现在server{}或location{}中,以及他可以携带的参数个数
             NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
             //ngx_command_t结构体中的set成员,
             //当在某个配置块中出现lcwsendfile配置项时,Nginx将会调用ngx_http_lcwsendfile方法
             //ngx_http_lcwsendfile方法将在下面实现
             ngx_http_lcwsendfile,
             //在配置文件中的偏移量conf
             NGX_HTTP_LOC_CONF_OFFSET,
             //offset通常用于使用预设的解析方法解析配置项,需要与conf配合使用
             0,
             //配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针
             NULL
        },
        //ngx_null_command是一个空的ngx_command_t结构,用来表示数组的结尾
        ngx_null_command
    };
    //ngx_http_module_t的8个回调方法,因为目前没有什么工作是必须在HTTP框架初始化
    //时完成的,所以暂时不必实现ngx_http_module_t的8个回调方法
    static ngx_http_module_t ngx_http_lcwsendfile_module_ctx =
    {
        NULL, // preconfiguration解析配置文件前调用
        NULL, // postconfiguration 完成配置文件解析后调用
    
        NULL, // create main configuration当需要创建数据结构用于存储main级别的
                    //(直属于http{}块的配置项)的全局配置项时
        NULL, // init main configuration常用于初始化main级别的配置项
    
        NULL, // create server configuration当需要创建数据结构用于存储srv级别的
                    //(直属于server{}块的配置项)的配置项时 
        NULL, // merge server configuration用于合并main级别和srv级别下的同名配置项
    
        NULL, // create location configuration 当需要创建数据结构用于存储loc级别的
                    //(直属于location{}块的配置项)的配置项时
        NULL // merge location configuration 用于合并srv和loc级别下的同名配置项
    };
    //定义lcwsendfile模块
    //lcwtest模块在编译时会被加入到ngx_modules全局数组中
    //Nginx在启动时,会调用所有模块的初始化回调方法
    //HTTP框架初始化时会调用ngx_http_module_t中的8个方法
    //HTTP模块数据结构
    ngx_module_t ngx_http_lcwsendfile_module =
    {
        NGX_MODULE_V1,//该宏为下面的ctx_index,index,spare0,spare1,spare2,spare3,version变量
                     //提供了初始化的值:0,0,0,0,0,0,1
        //ctx_index表示当前模块在这类模块中的序号
        //index表示当前模块在所有模块中的序号,Nginx启动时会根据ngx_modules数组设置各模块的index值
        //spare0 spare系列的保留变量,暂未使用
        //spare1
        //spare2
        //spare3
        //version模块的版本,便于将来的扩展,目前只有一种,默认为1
        &ngx_http_lcwsendfile_module_ctx, //ctx用于指向一类模块的上下文结构
        ngx_http_lcwsendfile_commands, //commands将处理nginx.conf中的配置项
        NGX_HTTP_MODULE, //模块的类型,与ctx指针紧密相关,取值范围是以下5种:
                                //NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE
        //以下7个函数指针表示有7个执行点会分别调用这7种方法,对于任一个方法而言,如果不需要nginx在某个是可执行它
        //那么简单地将他设为空指针即可
        NULL, //master进程启动时回调init_master
        NULL, //init_module回调方法在初始化所有模块时被调用,在master/worker模式下,
                                        //这个阶段将在启动worker子进程前完成
        NULL, //init_process回调方法在正常服务前被调用,在master/worker模式下,
                                        //多个worker子进程已经产生,在每个worker子进程的初始化过程会调用所有模块的init_process函数
        NULL, //由于nginx暂不支持多线程模式,所以init thread在框架代码中没有被调用过
        NULL, // exit thread,也不支持
        NULL, //exit process回调方法将在服务停止前调用,在master/worker模式下,worker进程会在退出前调用它
        NULL, //exit master回调方法将在master进程退出前被调用
        NGX_MODULE_V1_PADDING //这里是8个spare_hook变量,是保留字段,目前没有使用,Nginx提供了NGX_MODULE_V1_PADDING宏来填充
    };
    
    /******************************************************
    函数名:ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    参数:
    功能:lcwsendfile方法的实现
    *******************************************************/
    static char* ngx_http_lcwsendfile(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
        ngx_http_core_loc_conf_t *clcf;
        //首先找到lcwtest配置项所属的配置块,clcf貌似是location块内的数据
        //结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个
        //http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体
        clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
        //http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果
        //请求的主机域名、URI与lcwsendfile配置项所在的配置块相匹配,就将调用我们
        //实现的ngx_http_lcwsendfile_handler方法处理这个请求
        //ngx_http_lcwsendfile_handler将在下面实现
        clcf->handler = ngx_http_lcwsendfile_handler;
        return NGX_CONF_OK;
    }
    /******************************************************
    函数名:ngx_http_lcwsendfile_handler(ngx_http_request_t *r)
    参数:ngx_http_request_t结构体
    功能:ngx_http_lcwtest_handler方法的实现
    *******************************************************/
    static ngx_int_t ngx_http_lcwsendfile_handler(ngx_http_request_t *r)
    {
        //必须是GET或者HEAD方法,否则返回405 Not Allowed
        if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
        {
            return NGX_HTTP_NOT_ALLOWED;
        }
        //丢弃请求中的包体
        ngx_int_t rc = ngx_http_discard_request_body(r);
        if (rc != NGX_OK)
        {
            return rc;
        }
        //构造ngx_buf_t结构准备发送包体
        ngx_buf_t *b;
        //开辟ngx_buf_t缓冲区
        b = ngx_palloc(r->pool, sizeof(ngx_buf_t));
        //要打开的文件
        u_char* filename = (u_char*)"/mnt/hgfs/lcw_program/张杰 - My Sunshine.mp3";
        b->in_file = 1;//将in_file设置为1就表示ngx_buf_t缓冲区传送的是文件而不是内存
        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
        //打开文件传送文件描述符
        b->file->fd = ngx_open_file(filename, NGX_FILE_RDONLY | NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
        b->file->log = r->connection->log;
        b->file->name.data = filename;
        b->file->name.len = sizeof(filename) - 1;
        if (b->file->fd <= 0)//打开失败
        {
            return NGX_HTTP_NOT_FOUND;
        }
    
        //支持断点续传
        r->allow_ranges = 1;
    
        //获取文件长度ngx_file_info是linux系统中stat的一个宏
        if (ngx_file_info(filename, &b->file->info) == NGX_FILE_ERROR)
        {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        //设置缓冲区指向的文件块,这里是告诉Nginx从文件的file_pos偏移量开始发送文件
        //一直到达file_last偏移处截止
        b->file_pos = 0;
        b->file_last = b->file->info.st_size;
        //接下来要关闭文件句柄,否则会出现句柄泄露问题,于是定义如下结构体
        //ngx_pool_cleanup_add用于告诉HTTP框架,在请求结束时调用cln的handler方法清理资源
        ngx_pool_cleanup_t* cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
        if (cln == NULL)
        {
            return NGX_ERROR;
        }
        //赋值执行实际清理资源工作的回调方法,ngx_pool_cleanup_file方法的作用是把文件句柄关闭
        //ngx_pool_cleanup_file方法需要一个ngx_pool_cleanup_file_t类型的参数
        cln->handler = ngx_pool_cleanup_file;
        ngx_pool_cleanup_file_t *clnf = cln->data; 
        clnf->fd = b->file->fd;//文件句柄
        clnf->name = b->file->name.data;//文件名称
        clnf->log = r->pool->log;//日志对象
        //奇怪?这里怎么把ngx_pool_cleanup_file_t参数传给ngx_pool_cleanup_file方法?
        //设置返回的Content-Type。注意,ngx_str_t有一个很方便的初始化宏
        //ngx_string,它可以把ngx_str_t的data和len成员都设置好
        ngx_str_t type = ngx_string("audio/mp3");
        //设置返回状态码
        r->headers_out.status = NGX_HTTP_OK;
        //响应包是有包体内容的,所以需要设置Content-Length长度
        r->headers_out.content_length_n = b->file->info.st_size;
        //设置Content-Type
        r->headers_out.content_type = type;
        //发送http头部
        rc = ngx_http_send_header(r);
        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
        {
            return rc;
        }
        //构造发送时的ngx_chain_t结构体
        ngx_chain_t out;
        //赋值ngx_buf_t
        out.buf = b;
        //设置next为NULL
        out.next = NULL;
        //最后一步发送包体,http框架会调用ngx_http_finalize_request方法
        //结束请求
        return ngx_http_output_filter(r, &out);
    }

    配置文件也是差不多,编译方法也一样。不过我总是遇到一个问题,就是在windows新建的config文件有时候好像被识别不了,最后我在linux下写这个文件就可以了有点奇怪
    还有编译之后把模块加进nginx里,然后好像上次的那个就没有了,难道以后都得全部加进去吗?
    运行可以得到下面的结果:
    这里写图片描述
    下载成功,
    这里写图片描述
    是张杰的MySunshine,文件没有损坏,可以听

  • 相关阅读:
    Truck History(poj 1789)
    Highways poj 2485
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    115. Distinct Subsequences
    114. Flatten Binary Tree to Linked List
    113. Path Sum II
    109. Convert Sorted List to Binary Search Tree
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630514.html
Copyright © 2011-2022 走看看