pursue wind pursue wind
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
首页
Java
Python
数据库
框架
Linux
中间件
前端
计算机基础
DevOps
项目
面试
书
关于
归档
MacOS🤣 (opens new window)
GitHub (opens new window)
  • 基于Python轻松自建App服务器

    • 0App 与后端服务器通信方法简介
    • 1本小册要完成的通信场景功能
    • 2服务器端组件框架的选择与介绍
    • 3基于腾讯云的服务器端环境搭建
    • 4基于 Tornado 的 HTTP 服务器简介及代码组织框架
    • 5第一次数据请求 1:服务器接收用户注册信息
    • 6第一次数据请求 2:为用户处理模块增加 log 管理
    • 7第一次数据请求 3:将用户信息写入 MySQL 数据库
    • 8服务器接收客户端图片上传,并保存在硬盘中
      • 下载 JMeter
      • 安装 JMeter
      • 配置测试计划
        • 切换语言
        • 配置 HTTP 请求
      • 测试请求
        • 查看服务器端
      • 服务器端代码编写
        • 调用逻辑
        • 创建目录
        • 编写逻辑代码
      • 代码下载
      • 小结
    • 9服务器接收客户端请求,并返回 H5 页面
    • 10搭建基于 Nginx 的代理服务器
    • 11基于 HTTPS 的数据加密
    • 12大型 HTTP 服务器架构演进路线及思路
    • 13总结
  • 基于Python实现微信公众号爬虫

  • Xpath
  • python3 翻译
  • python3循环创建数据库表
  • python实用30个小技巧
  • pywin32
  • Python
  • 基于Python轻松自建App服务器
pursuewind
2020-11-23
目录

8服务器接收客户端图片上传,并保存在硬盘中

# 服务器接收客户端图片上传,并保存在硬盘中

前面几小节,我们已完成了 JSON 格式的纯数据交互,在 App 服务器端的设计中,我们难免会接收客户端图片的上传,并提供端图片下载。本小节将讲解,对于客户端向服务器端上传图片,服务器端将如何处理。简单交互过程如下。

同样,在这一小节中,我们也使用工具来代替 App 客户端模拟图片的上传。我们将要用到的工具是 JMeter,它是一个强大的工具,最为熟知的是 HTTP 的测试。这里我们不去深入了解 JMeter,而只是取其一个小功能 —— HTTP POST 图片的功能来完成讲解,读者如果感兴趣,可以自行学习拓展。

# 下载 JMeter

通过官网下载 JMeter:Download Apache JMeter (opens new window)

# 安装 JMeter

下载完成后,解压文件夹,进入 bin 目录,点击 jmeter.bat 进行 JMeter 的安装,安装成功后的界面如下。

# 配置测试计划

# 切换语言

依次选择“Options” -> “Choose Language” -> “Chinese (Simplified)”,如下图所示。

# 配置 HTTP 请求

右击 “Test Plan”,点击“添加” -> “Threads (Users)” -> “线程组”

右击 “线程组”,点击 “添加” -> “Sampler” -> “HTTP 请求”

在弹出的「HTTP 请求」框中进行如下设置:

  • 第 1~4 步,按照截图输入或选择;
  • 第 5 步,设定我们要上传图片(文件)的 URL 路径是 upload/file;
  • 第 6 步,选择 “Files Upload”;
  • 第 7 步,点击 ”添加”;
  • 第 8 步,点击 “浏览”,从本地随便选取一张图片(或本小节末尾提供的图片);
  • 第 9 步,输入该图片对象的参数名 image;
  • 第 10 步,输入我们上传的文件类型 image。

至此,请求页面已配置完毕,点击 “文件” -> “保存测试计划” 如下。

# 测试请求

点击如下 “启动” 按钮,测试是否请求成功

# 查看服务器端

此打印说明服务器端接收客户端请求成功,但由于 /upload/file 路径的代码未实现,服务器端返回 404 找不到路径。接下来,将进行服务器端图片上传代码编写。

# 服务器端代码编写

# 调用逻辑

与第 6 小节用户注册请求服务器端实现类似,客户端上传图片,进入 main.py,将调用 url_router 转发到 upload_url.py 中,在 upload_urls.py 中,对应的 URL 将调用 upload_views.py 的 UploadFileHandle 类,UploadFileHandle 为真正的代码处理逻辑,在校验用户信息正确的情况下,返回图片 URL 给客户端,客户端加载该图片。

# 创建目录

在 views 下面创建 upload 目录,在 upload 下创建 upload_urls.py、upload_views.py等文件。

在 log 目录下创建 upload 目录,用于存放日志。

图片一般会放在 static 目录下,在实际项目中,static 下的图片目录也是分层级的,此次讲解,我们将简化,把图片直接放在 static/image 目录下。创建 image 目录如下:

# 编写逻辑代码

修改 main.py 文件,增加 views.upload.upload_urls下的 url 路由,修改 handers 如下:

        handlers = url_wrapper([
        (r"/users/", include('views.users.users_urls')),
        (r"/upload/", include('views.upload.upload_urls'))
        ])
1
2
3
4

修改 upload_urls.py,输入如下代码:

#!/usr/bin/python3
# -*- coding:utf-8 -*-


from __future__ import unicode_literals
from .upload_views import (
    UploadFileHandle
)

urls = [
    #从/upload/file过来的请求,将调用upload_views里面的UploadFileHandle类
    (r'file', UploadFileHandle)
]
1
2
3
4
5
6
7
8
9
10
11
12
13

修改 upload_views.py,输入如下代码:

#! /usr/bin/python3
# -*- coding:utf-8 -*-

import tornado.web
import os
from tornado.escape import json_decode
import logging
from logging.handlers import TimedRotatingFileHandler
import json


#从commons中导入http_response及save_files方法
from common.commons import (
    http_response,
    save_files
)

#从配置文件中导入错误码
from conf.base import (
    ERROR_CODE,
    SERVER_HEADER
)

########## Configure logging #############
logFilePath = "log/upload/upload.log"
logger = logging.getLogger("Upload")  
logger.setLevel(logging.DEBUG)  
handler = TimedRotatingFileHandler(logFilePath,  
                                   when="D",  
                                   interval=1,  
                                   backupCount=30)  
formatter = logging.Formatter('%(asctime)s \
%(filename)s[line:%(lineno)d] %(levelname)s %(message)s',)  
handler.suffix = "%Y%m%d"
handler.setFormatter(formatter)
logger.addHandler(handler)
 
 
class UploadFileHandle(tornado.web.RequestHandler):
    """handle /upload/file request, upload image and save it to static/image/
    :param image: upload image
    """
        
    def post(self):
        try:
            #获取入参
            image_metas = self.request.files['image']
        except:
            #获取入参失败时,抛出错误码及错误信息
            logger.info("UploadFileHandle: request argument incorrect")
            http_response(self, ERROR_CODE['1001'], 1001)
            return 
            
        image_url = ""
        image_path_list = []
        if image_metas:
            #获取当前的路径
            pwd = os.getcwd()
            save_image_path = os.path.join(pwd, "static/image/")
            logger.debug("UploadFileHandle: save image path: %s" %save_image_path)
            #调用save_file方法将图片数据流保存在硬盘中
            file_name_list = save_files(image_metas, save_image_path)
            image_path_list = [SERVER_HEADER + "/static/image/" + i for i in file_name_list]
            ret_data = {"imageUrl": image_path_list}
            #返回图片下载地址给客户端
            self.write(json.dumps({"data": {"msg": ret_data, "code": 0}}))
        else:
            #如果图片为空,返回图片为空错误信息
            logger.info("UploadFileHandle: image stream is empty")
            http_response(self, ERROR_CODE['2001'], 2001)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

这里,我们从 common 导入 save_files 用于处理图片的保存,从 conf 的 base 中导入 SERVER_HEADER,定义了我们服务器的 URL 前缀。同时也看到,upload 和 users 的 Log 配置(如级别)是单独配置的,这样有助于单模块调试。下面修改 conf 目录下的 base.py 文件,增加如下:

完整代码如下:

#! /usr/bin/python3
# -*- coding:utf-8 -*-

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('mysql://root:pwd@demo@localhost:3306/demo?charset=utf8', encoding="utf8", echo=False)
BaseDB = declarative_base()

#服务器端 IP+Port,请修改对应的IP
SERVER_HEADER = "http://150.109.33.132:8000"

ERROR_CODE = {
    "0": "ok",
    #Users error code
    "1001": "入参非法",
    "1002": "用户已注册,请直接登录",
    
    "2001": "上传图片不能为空"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

commons.py 下,导入 os 模块( import os ),并增加 save_files 方法:

import os

def save_files(file_metas, in_rel_path, type='image'):
    """
    Save file stream to server
    """
    file_path = ""
    file_name_list = []
    for meta in file_metas:
        file_name = meta['filename']
        file_path = os.path.join( in_rel_path, file_name )
        file_name_list.append( file_name )
        #save image as binary
        with open( file_path, 'wb' ) as up:
            up.write( meta['body'] )
    return file_name_list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

至此,服务器端的代码已完成。再次从 JMeter 触发图片上传,在触发图片上传之前,我们先创建 JMeter 的结果树。所谓结果树,就是在触发请求之后,查看服务器端返回的结构。右击 “HTTP 请求”,依次选择“添加” -> ”监听器” -> “查看结果树”,如下图所示。

触发 JMeter 图片上传,点击 “察看结果树”,切到 “响应数据” 页面,可以看到服务器端返回的数据信息:

{"data": {"msg": {"imageUrl": ["http://150.109.33.132:8000/static/image/demo.jpg"]}, "code": 0}}
1

查看服务器端进程打印:

查看图片是否上传:

查看 log 是否成功写入:

此时,客户端就可以通过服务器端返回的图片 URL(http://150.109.33.132:8000/static/image/demo.jpg)加载图片了,在浏览器中输入图片 URL,查看加载是否成功。

# 代码下载

到目前为止,服务器端代码及图片如下:
demo9 (opens new window)

# 小结

至此,我们完成了服务器端图片上传的接收及图片 URL 返回,客户端根据服务器返回的图片 URL,即可加载该图片。这里没有写数据库的操作,读者可以尝试参考第 8 节的讲解,定义图片的 models,并将图片 URL 和其他信息写入数据库中。

Last Updated: 2023/02/14, 18:02:00
7第一次数据请求 3:将用户信息写入 MySQL 数据库
9服务器接收客户端请求,并返回 H5 页面

← 7第一次数据请求 3:将用户信息写入 MySQL 数据库 9服务器接收客户端请求,并返回 H5 页面→

Theme by Vdoing | Copyright © 2019-2023 pursue-wind | 粤ICP备2022093130号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
  • 飙升榜
  • 新歌榜
  • 云音乐民谣榜
  • 美国Billboard榜
  • UK排行榜周榜
  • 网络DJ