I used to use Nginx simply as a WebServer, but recently I took a look at its plugins and found that there is a lot to play with, in particular that a lot of work can be done by installing and configuring plugins without writing code. This article takes setting up a document server as an example to demonstrate how to use the Nginx plug-in.
Description of project
This project tries to use several modules of Nginx to build a document server to realize file uploading and browsing. A pure Nginx implementation is intended to make file uploading an independent base module, which can be used directly if other business modules need file management functions.
Project address: github.com/javanan/tms…
Installing a plug-in
The name of the | function | instruction |
---|---|---|
echo-nginx-module | Quick response content | echo |
nginx-upload-module | Upload a file | upload_xxx |
njs | Handle business logic in Javascript | js_xxx |
set-misc-nginx-module | Handle variables in the nginx.conf file | set_unescape_uri |
redis2-nginx-module | Redis client | redis2_xxx |
Configure Nginx
Environmental parameters
The environment variable | Parameter names | instructions |
---|---|---|
REDIS_KEY_UPLOAD_START_TIME | $redis_key_upload_start_time | The key used in Redis to hold the service startup time |
REDIS_KEY_UPLOAD_COUNTER | $redis_key_upload_counter | The key used in Redis to save the number of times a file has been uploaded |
REDIS_CHANNEL_UPLOAD | $redis_channel_upload | A channel in Redis that receives information about uploaded files |
LOCAL_UPLOAD_LOG | Local save upload file log, specified, not specified |
You need to add the following Settings to the nginx.conf file, using the env directive to specify the environment variables you want to use, and using the js_set directive to create and assign variables (there is no way to use environment variables directly in the configuration file).
env REDIS_KEY_UPLOAD_START_TIME;
env REDIS_KEY_UPLOAD_COUNTER;
env REDIS_CHANNEL_UPLOAD;
Copy the code
js_include /usr/local/nginx/njs/upload.js;
js_set $redis_key_upload_start_time var_redis_key_upload_start_time;
js_set $redis_key_upload_counter var_redis_key_upload_counter;
js_set $redis_channel_upload var_redis_channel_upload;
Copy the code
Access to the directory
We hope that we can through the Nginx direct access to a directory of files, for example: Nginx working directory files directory, in Nginx. Conf to add the following content:
location = /files/ {
root .;
autoindex on;
}
Copy the code
Upload a file
We use the nginx-upload-module plugin to upload files. After the installation is complete, add the following configuration to nginx.conf:
location /upload/ {
# specify where to store the uploaded file
upload_store /usr/local/nginx/files;
Set the forwarding information
upload_set_form_field $upload_field_name.name "$upload_file_name";
upload_set_form_field $upload_field_name.content_type "$upload_content_type";
upload_set_form_field $upload_field_name.path "$upload_tmp_path";
upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5";
upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size";
# Forward requests after file upload
upload_pass @upload_response;
# add_header "Content-Type" "text/html; charset=UTF-8";
# echo "echo: Upload done";
}
Copy the code
The upload_pass command forwards basic information about a file to a specified address. The address that receives the uploaded file information is named location, which is not matched by a regular and is used for internal forwarding (the NJS module explains how to do this later).
The uploaded file will be placed at the location specified by the upload_store directive. The file is an ascending string of digits, such as 0000000001 0000000002. If no further action is required, you can run the Echo command to return the result without forwarding the uploaded file data.
Handle file
Since you can’t specify the name of the uploaded file (no extension), and Nginx will start naming the file from scratch every time you restart it, possibly overwriting existing files, it is not convenient to use, so you want to change the name of the uploaded file. Here we use the NJS module and add the following configuration to nginx.conf:
js_include /usr/local/nginx/njs/upload.js;
Copy the code
location @upload_response {
js_content handle;
}
Copy the code
The file name consists of three parts: service startup time + number of files uploaded since startup + 1+ extension, for example, 20191224_124808_8.mp4. To achieve this requirement, the service startup time and file upload times need to be recorded. The simplest way to think about it is to make global variables, but there is no way to do that in Nginx. The idea was to write the data to a local file, but there would be concurrent reading problems, so Redis was used to save the data.
Access to Redis
Redis stores two data: 1. Startup time; 2. Counters. Each time a file is uploaded, a file name is created by combining the startup time and the counter. Get startup time with get command, get counter with incr command. Using the redis2-nginx-module module to turn nginx into a Redis client, we added the following configuration to nginx.conf:
location = /redis/counter {
redis2_query get $redis_key_upload_start_time;
redis2_query incr $redis_key_upload_counter;
redis2_pass redis:6379;
}
Copy the code
In addition to storing shared information, Redis is also used for event notification. After each file is processed, Redis uses its publish and subscribe mechanism to send a message about the uploaded file, so that other systems can receive this event if they need to be extended. Add the following configuration to nginx.conf:
location = /redis/publish {
set_unescape_uri $message $arg_message;
redis2_query publish $redis_channel_upload $message;
redis2_pass redis:6379;
}
Copy the code
The container is changed
For ease of use, we made the entire project into a Docker container. There are three containers: Nginx container, Redis container, and Redis-cli container, where Redis-cli is for setting initial values in Redis.
Set the time zone
The default time zone for alpine mirror is UTC, eight hours behind ours. As mentioned earlier, you need to have the service startup time as part of the upload file name, so you need to change the loss to Asia/Shanghai. Add the following content to the Dockerfile:
RUN sed -i 's? http://dl-cdn.alpinelinux.org/?https://mirrors.aliyun.com/?' /etc/apk/repositories && \
apk add -U tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
apk del tzdata
Copy the code
Redis initial value
We need to record the startup time in Redis every time the service is started. The simplest way to think of is to run the following command with Redis – CLI after the service is started.
date +'%Y%m%d_%H%M%S' | xargs redis-cli -h redis set uploadBootAt
Copy the code
The environment variable
Specify the value of the environment variable in the docker-comemage. yml file, which can be overridden by the docker-comemage. overlay. yml file if desired.
environment:
- REDIS_KEY_UPLOAD_BOOT_AT=upload:start_time
- REDIS_KEY_UPLOAD_COUNTER=upload:counter
- REDIS_CHANNEL_UPLOAD=upload:event
Copy the code
other
volumes:
- ./nginx/nginx.conf:/usr/local/nginx/conf/nginx.conf:ro
- ./nginx/html:/usr/local/nginx/html
- ./nginx/njs:/usr/local/nginx/njs
- ./upload:/usr/local/nginx/files
Copy the code
Here’s a lesson to share. I wanted to upload the file to TMP and rename it to the directory named volumes. However, this requires the file to be moved between the two devices, which is not allowed, so the file is directly uploaded to the specified directory and renamed on the spot.
Set the connection to Redis in nginx.conf.
location = /redis/counter {
redis2_query get $redis_key_upload_start_time;
redis2_query incr $redis_key_upload_counter;
redis2_pass redis:6379;
}
Copy the code
This requires a connection between the two containers, but we don’t know what the address is beforehand. The solution is to specify it in docker-comemage.yml.
Containers for the linked service are reachable at a hostname identical to the alias, or the service name if no alias was specified.
nginx:
...
links:
- redis
Copy the code
JS code description
Nginx. Conf file
env REDIS_KEY_UPLOAD_START_TIME;
Copy the code
js_set $redis_key_upload_start_time var_redis_key_upload_start_time;
Copy the code
njs/upload.js
//
function var_redis_key_upload_start_time(r) {
return process.env.REDIS_KEY_UPLOAD_START_TIME;
}
Copy the code
The js_set directive solves the problem of assigning values to environment variables that cannot be referenced in the nginx.conf file.
r.subrequest("/redis/counter", { method: "GET" }, function(res) {
......
});
Copy the code
You can make a call with the subRequest method, but not with a named location.
var fs = require("fs");
fs.renameSync(oFileData.path, newpath);
Copy the code
The FS module enables simple file operations. However, you cannot use require to call your own modules.