资源服务器搭建

闲的没事,想着自己搭建一个资源服务器可能以后能用到。选择使用Nginx + Golang搭建,结果这一搞就搭进去了三个晚上,都是泪啊。所以决定写一篇博客来祭奠一下逝去的三个晚上。

环境依赖

  • Nginx我安装的版本为nginx version: nginx/1.12.2
  • upload-nginx-module标准的Nginx是不包含upload-nginx-module的,所在编译的时候记得添加
  • Golang环境go version go1.8.1 darwin/amd64

上面提及这些具体怎么安装,我就不在这详细描述了,大家可以自行Google解决

资源服务器搭建思路

  • 客户端上传文件资源到Nginx服务器
  • Nginx服务器将资源保存在临时目录,将请求转发给Golang业务服务器
  • 业务服务器将资源移动位置,重命名操作
  • 业务服务器返回处理结果给客户端

当然还有一些其他设计思路,比如可以将图片直接上传给业务服务器,再由业务服务器传给资源服务器。或者直接将图片传给业务服务器,等客户端向资源服务器请求资源的时候,再由资源服务器来业务服务器取。但是后面说的这两种,如果要做断点续传,都需要业务服务器自己处理一些逻辑。但是第一种设计,就可以把断电续传等功能都交由资源服务器处理了,因此我选择了第一种设计。

nginx配置

    
        location /upload {
            #上传完成后后端接受处理文件 
            upload_pass @uploadImageGo;
            #文件上传路径,文件临时路径 
            upload_store /Users/liuhu/Desktop/nginxRoot/www/upload/images; 
            #上传速度限制
            upload_limit_rate 2048k;
            #文件读写权限 
            upload_store_access user:rw;
            #表单中提交的信息
            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";
            #转发的时候只传submit和description
            #upload_pass_form_field "^submit$|^description$";
            #打开开关,意思就是把前端请求的参数会传给后端
            upload_pass_args on; 
            #将表单中所有信息传递给后端使用,客户端提交的信息也传递
            upload_pass_form_field "^.*$";
            #发生这些错无的时候删除资源
            upload_cleanup 400 404 499 500-505; 
        }
        
        location @uploadImageGo {
          proxy_method POST;
            proxy_pass http://localhost:8081;
        }

注意:必须使用Post请求/uoload接口,不能nginx会直接报错

业务服务器处理

三个晚上就坑在了这里

  • 当时调用r.ParseForm()之后,就直接调用r.Form发现Form是一个空的Map,但是Content-Length又是大于0

  • 然而想看到底有没有穿参数过来,就改了nginx的几个配置。比如少传几个参数或者多传几个参数,发现Content-Length又是有变化的,证明参数是穿过来的

  • 没有办法只能就把整个httpdump出来了

    • dump整个http的代码如下

          httpData, _ := httputil.DumpRequest(r, true)
          log.Println(string(httpData))
      
    • dump的结果是

          POST /upload HTTP/1.0
          Host: localhost:8081
          Connection: close
          Accept: */*
          Connection: close
          Content-Length: 778
          Content-Type: multipart/form-data; boundary=------------------------767b3fb9efcce66b
          User-Agent: curl/7.54.0
      
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="image.name"
      
          vim_keymap.png
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="image.content_type"
      
          application/octet-stream
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="image.path"
      
          /Users/liuhu/Desktop/nginxRoot/www/upload/images/0006817569
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="image.md5"
      
          62519e639e490a69d0106bceb7e0f184
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="image.size"
      
          176536
          --------------------------767b3fb9efcce66b
          Content-Disposition: form-data; name="sss"
      
          sss
          --------------------------767b3fb9efcce66b--
      
    • 发现参数全部放在了Content-Disposition里面,并且参数还是用一个动态的boundary进行的分割。

  • 通过dump信息发现请求的Content-Typemultipart/form-data。因此查了一下Golang的文档,发现multipart/form-data需要先将整个请求的body解析到内存中,才能从Form中获取到相应的字段。

    ParseMultipartForm parses a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files. ParseMultipartForm calls ParseForm if necessary. After one call to ParseMultipartForm, subsequent calls have no effect.

  • 服务端的主要代码如下

    if r.Method == "POST" {
        r.ParseMultipartForm(32 << 20)
        log.Println(r.Form)
        //处理图片
        //todo
        w.Write([]byte("上传成功"))
    }
    

你可能感兴趣的:(资源服务器搭建)