博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Nginx】磁盘文件写入飞地发
阅读量:4674 次
发布时间:2019-06-09

本文共 7257 字,大约阅读时间需要 24 分钟。

文章继续。什么时候Nginx当用户请求一个文件,这将无法读取该文件的内容加载到内存,然后从内存发送,但电话sendfile况下,从内核直接发送出去。这样做显然效率要更高。Nginx也为我们封装好了一系列的接口。以下就来说明怎样发送一个磁盘文件给client。

和从内存直接发送数据最大的不同在于ngx_buf_t缓冲区的设置方法。ngx_buf_t结构体的定义例如以下:
struct ngx_buf_s {    ....    off_t            file_pos;     // 文件起始位置    off_t            file_last;    // 文件结束位置    ....    ngx_file_t      *file;     // 引用的文件    ....};
ngx_file_t表示一个文件:
typedef struct ngx_file_s        ngx_file_t;struct ngx_file_s {    ngx_fd_t                   fd;              // 文件描写叙述符    ngx_str_t                  name;            // 文件名称    ngx_file_info_t            info;            // 文件相关信息,相当于stat结构体     off_t                      offset;          // 告诉Nginx处理到文件何处了。一般不使用    off_t                      sys_offset;      // 文件偏移量,一般不使用     ngx_log_t                 *log;             // 日志对象 #if (NGX_HAVE_FILE_AIO)    ngx_event_aio_t           *aio;#endif     unsigned                   valid_info:1;    // 未使用    unsigned                   directio:1;      // 发送大文件时设为1};
发送响应包体之前就须要对上述部分成员进行设置。以确定须要发送的文件和相关信息。
为了防止内存泄漏,HTTP框架须要在发送响应结束之后关闭文件描写叙述符,这是须要定义一个ngx_pool_cleanup_t结构体:
struct ngx_pool_cleanup_s {    ngx_pool_cleanup_pt   handler;  // 运行实际清理工作的回调方法    void                 *data;     // 回调方法的參数    ngx_pool_cleanup_t   *next;     // 下一个清理对象};
清理文件句柄的完整代码例如以下:
// 用于告诉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; cln->handler = ngx_pool_cleanup_file;       // ngx_pool_cleanup_file专用于关闭文件句柄     ngx_pool_cleanup_file_t  *clnf = cln->data; // cln->data为上述回调函数的參数clnf->fd = b->file->fd;clnf->name = b->file->name.data;clnf->log = r->pool->log;
handler成员设为了ngx_pool_cleanup_file函数,这是Nginx本身内置的一个函数:
voidngx_pool_cleanup_file(void *data){    ngx_pool_cleanup_file_t  *c = data;     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",                   c->fd);     if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {    // 关闭文件描写叙述符        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,                      ngx_close_file_n " \"%s\" failed", c->name);    }}
清理函数主要工作就是关闭文件描写叙述符,它接受的參数为一个ngx_pool_cleanup_file_t指针。该结构体定义例如以下:
typedef struct {    ngx_fd_t              fd;   // 文件描写叙述符    u_char               *name; // 文件名称    ngx_log_t            *log;  // 日志对象} ngx_pool_cleanup_file_t;
这三个成员和ngx_buf_t.ngx_file_t中的下面三个成员一一相应:
  • ngx_buf_t.ngx_file_t.df
  • ngx_buf_t.ngx_file_t.name
  • ngx_buf_t.ngx_file_t.log
所以我们仅仅须要用ngx_buf_t内的成员对ngx_pool_cleanup_file_t结构体赋值就可以。
程序总体框架和从内存发送数据同样,以下是完整的代码:
#include 
#include
#include
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r); static ngx_command_t ngx_http_mytest_commands[] ={ { ngx_string("mytest"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS, ngx_http_mytest, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command}; static ngx_http_module_t ngx_http_mytest_module_ctx ={ NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */}; ngx_module_t ngx_http_mytest_module ={ NGX_MODULE_V1, &ngx_http_mytest_module_ctx, /* module context */ ngx_http_mytest_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING}; static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){ ngx_http_core_loc_conf_t *clcf; // 首先找到mytest配置项所属的配置块,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与mytest配置项所在的配置块相匹配,就将调用我们 // 实现的ngx_http_mytest_handler方法处理这个请求 clcf->handler = ngx_http_mytest_handler; return NGX_CONF_OK;} static ngx_int_t ngx_http_mytest_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 *b; b = ngx_palloc(r->pool, sizeof(ngx_buf_t)); u_char* filename = (u_char*)"/tmp/test.txt"; // 要打开的文件名称 b->in_file = 1; // 设置为1表示缓冲区中发送的是文件 // 分配代表文件的结构体空间。file成员表示缓冲区引用的文件 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; // name成员表示文件名称称 b->file->name.len = sizeof(filename) - 1; if (b->file->fd <= 0) return NGX_HTTP_NOT_FOUND; r->allow_ranges = 1; //支持断点续传 // 获取文件长度,ngx_file_info方法封装了stat系统调用 // info成员就表示stat结构体 if (ngx_file_info(filename, &b->file->info) == NGX_FILE_ERROR) return NGX_HTTP_INTERNAL_SERVER_ERROR; // 设置缓冲区指向的文件块 b->file_pos = 0; // 文件起始位置 b->file_last = b->file->info.st_size; // 文件结束为止 // 用于告诉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; cln->handler = ngx_pool_cleanup_file; // ngx_pool_cleanup_file专用于关闭文件句柄 ngx_pool_cleanup_file_t *clnf = cln->data; // cln->data为上述回调函数的參数 clnf->fd = b->file->fd; clnf->name = b->file->name.data; clnf->log = r->pool->log; // 设置返回的Content-Type // 注意,ngx_str_t有一个非常方便的初始化宏 // ngx_string,它能够把ngx_str_t的data和len成员都设置好 ngx_str_t type = ngx_string("text/plain"); //设置返回状态码 r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->file->info.st_size; // 正文长度 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; out.buf = b; out.next = NULL; //最后一步发送包体,http框架会调用ngx_http_finalize_request方法 return ngx_http_output_filter(r, &out);}
依据程序的指示。在/tmp文件夹内创建test.txt文件,内容例如以下:
执行结果:
Nginxserver成功返回了test.txt文件内的内容。
參考:
《深入理解Nginx》 P107-P112.

版权声明:本文博客原创文章,博客,未经同意,不得转载。

转载于:https://www.cnblogs.com/yxwkf/p/4642517.html

你可能感兴趣的文章
datagrid单元格格式化样式化
查看>>
转:在Nginx上配置多个站点
查看>>
javascript 技巧总结积累1-108条(正在积累中)
查看>>
为什么尽量避免使用 CSS 表达式
查看>>
[转]这才是真正的3D显示!Leap Motion推出次毫米级3D手动控制技术,让人手和影像融为一体...
查看>>
poj 3621 Sightseeing Cows 01分数规划
查看>>
bzoj 2821:作诗 分块
查看>>
如何让eclipse恢复默认布局
查看>>
简析.NET Core 以及与 .NET Framework的关系
查看>>
【模板题】欧拉回路
查看>>
QEMU+GDB调试方法
查看>>
洛谷 P1272 重建道路(树形DP)
查看>>
sql
查看>>
ShellExecute与ShellExecuteEx的用法
查看>>
第16课 “远程 Git文档库” 的基础操作
查看>>
总结oninput、onchange与onpropertychange事件的使用方法和差别
查看>>
go语言的特点
查看>>
leetcode : Remove Duplicates from Sorted List II [基础]
查看>>
常用正则汇集
查看>>
关于小范围整数N拆解成2的幂相加的个数
查看>>