Nginx代理用户上传的文件在前端显示

测试平台新增需求,需要将项目注册过程中的验证码图片识别并记录

实现逻辑:

1、新建SVG模型,用于保存SVG验证码图片

2、实现SVG源码转PNG,调用三方接口识别验证码,并将识别结果记录在数据库

3、SVG转PNG后,保存在测试平台的文件系统中,根据Nginx进行反向代理,在前端直接展示SVG图片及验证结果

后端代码实现:

  • 新增模型:
class SvgValidate(models.Model):
    """svg图片识别表"""
    svg_data = models.TextField('svg源码', help_text='svg源码', blank=True, default='')
    svg = models.ImageField('svg转png图片文件', help_text='svg转png图片')
    result = models.CharField('图片识别结果', help_text='识别结果', max_length=20)
    create_by = models.CharField('创建人', help_text='创建人', blank=True, default='', max_length=20)
    create_time = models.DateTimeField('创建时间', help_text='创建时间', auto_now_add=True)
    project = models.ForeignKey('projects.Project', on_delete=models.CASCADE, verbose_name='所属项目', help_text='所属项目')
    
    class Meta:
        db_table = 'tb_svg_file'
        verbose_name = 'svg图片图片识别'
        verbose_name_plural = verbose_name
        ordering = ['-id']
        
    def __str__(self) -> str:
        return self.svg.name
  • 编写视图:
class SvgViewSet(viewsets.GenericViewSet,
                 viewsets.mixins.ListModelMixin,
                 viewsets.mixins.DestroyModelMixin,
                 viewsets.mixins.CreateModelMixin
                 ):
    """SVG识别视图"""
    queryset = SvgValidate.objects.all()
    serializer_class = SvgSerializers
    filterset_fields = ['project']
    
    
    def perform_create(self, serializer):
        try:
            now = datetime.datetime.now()
            formatted_time = now.strftime("%Y-%m-%d_%H-%M-%S")
            svgStr = self.request.data['svg_data'].replace('\\',  '').replace('\t', '').replace('\n', '')
            svg_path = svg_to_png(svgStr)
            result = discern_captcha(img=svg_path)
            # svg_path = str(settings.MEDIA_ROOT / 'captcha.png')
            try:
                shutil.copy2(svg_path, os.path.abspath(settings.MEDIA_ROOT / 'captcha'))
                # 重新命名文件
                copied_file = os.path.join(os.path.abspath(settings.MEDIA_ROOT / 'captcha'), os.path.basename(svg_path))
                os.rename(copied_file, os.path.join(os.path.abspath(settings.MEDIA_ROOT / 'captcha'), f'captcha{formatted_time}.png'))
                new_path = os.path.join(os.path.abspath(settings.MEDIA_ROOT / 'captcha'), f'captcha{formatted_time}.png')
            except:
                raise ValidationError(f'copy文件出错:{svg_path}')
            
            # django在linux上运行时不允许出现绝对路径的文件操作
            new_path = new_path.replace('\\', '/')
            if new_path.startswith('/'):
                relative_path = os.path.relpath(new_path, settings.BASE_DIR)
            else:
                relative_path = new_path
            
            with open(relative_path, 'rb') as f:
                svgFile = File(f)
                try:
                    svgInstance = SvgValidate(
                        svg=svgFile, 
                        project=serializer.validated_data['project'],
                        svg_data = svgStr,
                        result = result,
                        create_by = self.request.user.username)
                    svgInstance.save()
                    svgFile.closed
                except:
                    raise ValidationError(f'序列化实例对象错误!:{svgInstance}')
        except:
            raise ValidationError(f'提交的参数有误!:{svg_path}')

实现逻辑:通过svg_to_png方法将svg源码转换成png,并保存在项目的tools目录下,复制图片到django项目配置的MEDIA_ROOT路径下,由于我们使用的ImageField字段,django不会保存除MEDIA_ROOT外的文件。另外还需注意,项目部署后再django容器中运行时,django自3.24版本以后新增针对潜在目录遍历漏洞的补丁,此处如果使用的文件路径包含 '..'或者是绝对路径,会抛出SuspiciousFileOperation异常,见如下源码,所以我们这里需要将文件的绝对路径改为相对路径,再通过open方法打开保存在ImageFeild字段中。

# My own comment: This is from 3.2.4 source, 
# but it is identical to 3.2 source as best I can tell

    if allow_relative_path:
        # Use PurePosixPath() because this branch is checked only in
        # FileField.generate_filename() where all file paths are expected to be
        # Unix style (with forward slashes).
        path = pathlib.PurePosixPath(name)
        if path.is_absolute() or '..' in path.parts:
            raise SuspiciousFileOperation(
                "Detected path traversal attempt in '%s'" % name
            )
{
        "id": 39,
        "svg_data": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"150\" height=\"50\" viewBox=\"0,0,150,50\"><path fill=\"#333\" d=\"M28.62 27.69L28.64 27.71L28.64 27.71Q25.14 27.64 24.04 30.99L24.02 30.97L24.07 31.03Q23.78 32.18 23.66 33.24L23.48 33.07L23.52 33.10Q23.27 35.10 24.26 36.43L24.33 36.49L24.34 36.51Q25.65 37.66 28.12 37.70L28.17 37.75L28.21 37.79Q29.87 37.67 31.28 36.52L31.44 36.68L31.47 36.71Q32.76 35.45 32.76 33.66L32.69 33.59L32.82 33.72Q32.84 33.40 32.77 33.02L32.62 32.87L32.78 33.03Q32.57 27.91 28.76 27.84ZM28.49 40.12L28.55 40.19L28.52 40.16Q24.66 40.33 22.87 38.32L23.01 38.46L22.87 38.31Q22.80 39.50 22.34 40.87L22.27 40.80L22.25 40.78Q20.38 41.61 19.12 42.18L19.05 42.10L19.02 42.08Q21.32 36.23 21.32 29.72L21.34 29.74L21.20 29.60Q21.14 21.25 17.72 13.40L17.90 13.58L17.73 13.42Q20.19 14.89 21.53 15.35L21.56 15.38L21.43 15.25Q23.56 21.91 23.71 27.74L23.72 27.74L23.79 27.81Q25.28 25.19 29.01 25.22L28.97 25.18L28.89 25.11Q32.22 25.24 33.70 27.10L33.72 27.12L33.61 27.01Q35.39 29.25 35.54 32.83L35.62 32.91L35.60 32.89Q35.73 34.12 35.66 35.72L35.59 35.65L35.65 35.71Q35.62 37.17 34.75 38.31L34.66 38.23L34.72 38.28Q32.88 39.91 28.54 40.17ZM34.08 42.75L34.09 42.76L34.02 42.68Q35.63 42.82 37.12 42.13L36.94 41.96L37.09 42.11Q38.14 40.75 38.18 38.81L38.25 38.88L38.15 38.79Q38.24 38.38 38.09 37.20L38.06 37.17L38.03 37.14Q37.99 36.49 37.87 35.58L37.87 35.58L37.84 35.55Q37.01 30.75 35.45 28.70L35.43 28.68L35.39 28.64Q35.36 28.57 34.56 27.70L34.72 27.85L34.67 27.81Q34.32 27.41 33.79 26.62L33.81 26.64L33.98 26.81Q32.36 24.85 29.01 24.89L28.95 24.83L28.91 24.79Q26.92 24.89 25.67 25.50L25.56 25.39L25.59 25.42Q25.36 21.00 24.75 17.81L24.73 17.79L24.77 17.82Q23.93 17.56 22.45 17.18L22.54 17.27L22.48 17.21Q22.28 16.41 21.83 14.96L21.87 15.00L21.77 14.91Q19.50 14.27 17.25 12.71L17.11 12.56L17.21 12.67Q20.86 20.73 20.86 29.53L20.85 29.52L20.90 29.56Q20.92 36.56 18.45 42.76L18.38 42.69L18.43 42.75Q19.23 42.52 20.57 41.95L20.56 41.94L20.42 41.80Q20.28 42.65 19.67 44.17L19.65 44.16L19.70 44.20Q21.71 43.33 24.49 42.79L24.47 42.77L24.73 40.82L24.80 40.90Q27.09 42.57 34.01 42.68ZM30.20 29.96L30.22 29.98L30.19 29.95Q31.35 30.08 31.89 30.46L31.76 30.34L31.78 30.36Q32.20 30.97 32.43 32.99L32.33 32.89L32.27 32.82Q32.61 34.84 31.35 36.16L31.38 36.18L31.26 36.06Q30.09 37.46 28.15 37.35L28.05 37.25L28.10 37.30Q27.28 37.55 25.80 37.05L25.74 37.00L25.78 37.04Q25.42 36.41 25.42 35.87L25.46 35.92L25.51 35.96Q25.48 32.85 27.04 31.26L27.06 31.27L27.06 31.31L27.12 31.37Q28.47 29.90 30.22 29.98Z\"/><path fill=\"#222\" d=\"M96.08 34.74L95.99 34.64L96.08 34.74Q96.02 38.26 92.86 39.55L92.94 39.63L92.87 39.55Q91.00 40.39 86.58 40.54L86.64 40.60L86.51 40.47Q81.69 40.56 79.44 39.38L79.47 39.40L79.48 39.42Q77.65 38.12 77.80 34.77L77.91 34.88L77.76 34.73Q77.81 34.12 78.00 32.15L78.00 32.15L77.99 32.14Q78.21 30.65 78.21 29.66L78.26 29.71L78.14 29.58Q78.10 21.55 73.91 14.13L73.96 14.17L74.08 14.30Q75.16 14.88 78.13 15.94L78.02 15.84L78.06 15.88Q81.00 22.66 81.00 30.05L81.05 30.10L80.94 29.99Q80.94 30.60 80.84 31.78L80.89 31.82L80.85 31.78Q80.81 33.02 80.81 33.63L80.78 33.60L80.79 33.61Q80.75 35.85 82.31 36.84L82.38 36.91L82.33 36.86Q83.99 37.68 86.96 37.68L87.09 37.81L86.91 37.63Q89.69 37.79 91.33 36.61L91.19 36.47L91.16 36.44Q93.03 35.11 92.88 32.60L92.90 32.62L93.05 32.77Q92.87 31.30 92.87 29.97L92.82 29.91L92.91 30.00Q92.85 22.90 95.24 16.39L95.26 16.41L97.34 15.74L97.33 15.74Q98.44 15.59 99.54 15.13L99.53 15.11L99.59 15.18Q95.70 22.14 95.70 29.71L95.78 29.79L95.75 29.76Q95.78 30.63 95.91 32.27L95.82 32.17L95.88 32.23Q96.02 33.87 96.02 34.67ZM98.12 35.52L98.09 35.49L98.19 35.59Q97.34 30.67 98.06 25.56L98.01 25.52L97.97 25.47Q98.76 20.33 101.16 15.95L101.25 16.03L101.11 15.90Q100.59 16.29 99.14 16.93L99.04 16.84L99.12 16.92Q99.52 16.10 100.32 14.54L100.37 14.59L100.36 14.58Q96.75 15.65 94.85 15.99L94.95 16.09L94.94 16.09Q92.56 22.57 92.56 29.84L92.61 29.89L92.54 29.82Q92.50 31.15 92.57 32.56L92.70 32.69L92.72 32.70Q92.70 34.93 90.91 36.15L90.96 36.20L91.02 36.26Q89.54 37.33 87.03 37.37L87.03 37.37L86.97 37.31Q84.17 37.33 82.72 36.57L82.70 36.54L82.70 36.54Q82.66 36.09 82.58 35.63L82.55 35.59L82.66 35.71Q82.54 35.13 82.66 34.64L82.70 34.68L82.71 34.69Q83.01 32.44 83.01 30.12L82.89 30.00L82.85 29.95Q82.90 24.18 81.38 18.28L81.30 18.21L81.30 18.21Q80.57 18.09 79.17 17.74L79.23 17.81L79.19 17.77Q78.83 16.49 78.37 15.58L78.37 15.58L78.46 15.67Q75.75 14.90 73.35 13.53L73.38 13.56L73.28 13.46Q77.86 21.00 77.86 29.72L77.89 29.76L77.84 29.70Q77.92 31.42 77.76 33.13L77.80 33.16L77.82 33.19Q77.43 35.43 77.51 36.61L77.59 36.69L77.53 36.62Q77.70 38.58 78.95 39.73L78.96 39.73L79.06 39.84Q79.92 42.18 84.56 42.56L84.53 42.53L84.54 42.53Q86.74 42.79 88.87 42.87L88.84 42.83L88.80 42.79Q94.15 42.97 96.93 42.02L96.99 42.08L97.10 42.18Q98.50 41.12 98.50 39.06L98.68 39.24L98.66 39.22Q98.67 38.09 98.22 35.62Z\"/><path fill=\"#222\" d=\"M55.17 39.87L55.22 39.92L55.21 39.91Q51.11 39.43 49.28 37.98L49.28 37.98L49.22 37.92Q47.40 36.47 46.98 33.47L46.93 33.42L46.87 33.36Q46.97 33.12 46.63 28.36L46.66 28.39L46.67 28.41Q46.71 27.23 46.64 25.97L46.48 25.81L46.54 25.88Q46.40 20.86 48.61 19.11L48.66 19.16L48.62 19.12Q51.35 17.09 59.12 16.41L59.11 16.40L58.98 16.27Q60.28 16.08 61.91 16.12L62.06 16.27L62.02 16.23Q62.09 16.29 65.06 16.29L64.94 16.18L65.02 16.26Q65.75 16.15 67.47 16.31L67.46 16.30L67.42 16.26Q66.98 17.27 65.96 20.35L65.93 20.33L66.08 20.48Q63.98 19.25 60.97 19.25L60.97 19.25L60.95 19.24Q60.17 19.22 59.37 19.29L59.34 19.25L59.42 19.33Q54.18 19.66 51.67 21.64L51.73 21.70L51.72 21.69Q49.61 23.20 49.46 26.55L49.46 26.54L49.55 26.63Q49.44 27.18 49.48 28.43L49.55 28.50L49.54 28.49Q49.66 33.33 52.06 35.43L51.99 35.36L52.12 35.49Q54.33 37.47 59.31 37.77L59.27 37.72L59.23 37.68Q62.36 37.85 65.36 36.06L65.27 35.96L65.39 36.08Q66.03 38.86 66.64 40.23L66.52 40.11L66.51 40.10Q64.41 40.36 62.78 40.32L62.90 40.45L62.91 40.46Q58.34 40.30 55.22 39.92ZM70.34 43.66L70.38 43.70L70.25 43.57Q68.57 40.67 67.89 38.08L67.78 37.98L67.78 37.98Q67.35 38.24 66.36 38.62L66.54 38.79L66.38 38.63Q66.06 37.86 65.87 37.02L65.90 37.05L65.49 35.30L65.48 35.30Q62.22 37.37 59.10 37.25L59.11 37.26L59.07 37.22Q54.95 37.18 52.63 35.42L52.52 35.31L52.50 35.30Q51.47 33.58 51.55 30.65L51.51 30.61L51.58 30.68Q51.64 26.10 54.11 23.97L54.07 23.92L54.12 23.98Q56.15 21.97 60.84 21.36L60.97 21.50L60.96 21.49Q61.68 21.30 62.41 21.30L62.36 21.25L62.46 21.35Q65.17 21.29 67.27 22.81L67.38 22.92L67.37 22.91Q67.80 20.72 69.02 17.48L68.98 17.44L69.06 17.51Q68.80 17.52 68.25 17.47L68.14 17.36L68.21 17.43Q67.75 17.46 67.48 17.46L67.31 17.29L67.45 17.43Q67.68 16.94 68.13 15.95L68.07 15.88L68.12 15.93Q67.91 15.95 65.42 15.88L65.33 15.79L65.43 15.89Q62.77 15.65 62.13 15.69L62.28 15.84L62.17 15.73Q51.70 16.11 48.23 18.70L48.26 18.72L48.12 18.58Q46.11 20.41 46.11 24.34L46.25 24.48L46.30 24.53Q46.14 25.47 46.25 28.17L46.23 28.16L46.35 28.27Q46.48 31.83 46.71 33.54L46.62 33.45L46.64 33.47Q47.03 36.61 48.63 38.13L48.65 38.15L48.60 38.10Q49.18 38.87 50.44 40.01L50.35 39.92L50.33 39.91Q53.61 41.70 58.86 42.46L58.97 42.57L58.99 42.59Q65.27 43.39 70.26 43.58Z\"/><path d=\"M3 8 C79 9,55 30,133 18\" stroke=\"#555\" fill=\"none\"/><path fill=\"#333\" d=\"M115.31 40.32L115.20 40.21L115.22 40.23Q111.74 40.32 110.10 39.79L110.05 39.73L110.05 39.74Q108.18 39.20 107.88 35.81L107.89 35.83L109.38 34.80L109.27 34.69Q110.09 34.26 110.85 33.77L110.68 33.60L110.78 33.69Q110.60 35.68 112.20 36.90L112.01 36.71L112.00 36.70Q113.35 37.79 115.48 37.60L115.58 37.69L115.47 37.58Q119.81 37.12 119.62 33.58L119.67 33.64L119.74 33.71Q119.63 31.50 117.00 30.28L116.93 30.21L116.93 30.21Q114.09 29.16 111.73 27.94L111.66 27.87L111.76 27.97Q109.16 26.59 108.17 21.91L108.25 21.98L108.13 21.87Q108.04 21.44 107.93 20.68L107.93 20.68L108.00 20.75Q107.84 19.90 107.92 19.33L107.86 19.28L108.00 19.41Q108.09 17.76 109.35 17.30L109.36 17.32L109.44 17.39Q111.71 16.39 115.71 16.58L115.80 16.67L115.75 16.62Q117.56 16.72 118.36 16.80L118.28 16.72L118.34 16.77Q119.91 17.05 120.98 17.51L121.03 17.56L120.99 17.52Q123.12 18.02 123.35 20.49L123.33 20.46L123.21 20.35Q122.27 21.16 120.06 22.49L119.94 22.37L119.97 22.41Q119.51 19.35 115.13 19.35L115.17 19.39L115.30 19.52Q113.36 19.48 112.37 20.17L112.31 20.11L112.28 20.08Q111.16 20.63 111.39 22.42L111.43 22.46L111.47 22.50Q111.62 24.60 114.59 26.12L114.56 26.09L114.62 26.15Q115.24 26.46 119.73 28.10L119.63 28.00L119.56 27.93Q122.36 29.59 122.78 33.96L122.65 33.83L122.60 33.79Q122.67 34.01 122.74 35.26L122.81 35.33L122.66 35.18Q122.90 38.12 121.34 39.22L121.21 39.10L121.21 39.10Q119.48 40.19 115.30 40.30ZM117.58 42.66L117.59 42.67L117.51 42.60Q118.94 42.65 120.92 42.65L120.94 42.67L120.99 42.73Q123.16 42.79 124.41 42.38L124.35 42.31L124.27 42.24Q125.55 41.46 125.48 39.67L125.50 39.69L125.44 39.63Q125.40 38.65 125.02 36.59L125.00 36.57L124.99 36.55Q124.08 31.88 121.99 30.02L121.98 30.00L121.94 29.97Q121.29 28.52 119.96 27.80L119.87 27.71L114.71 25.78L114.65 25.73Q114.30 25.52 113.84 25.30L113.80 25.25L113.74 24.92L113.71 24.55L113.66 24.51Q113.53 23.11 114.67 22.51L114.73 22.57L114.76 22.60Q115.44 21.94 117.15 21.75L117.22 21.82L117.24 21.84Q118.27 21.61 119.41 22.07L119.47 22.13L119.43 22.09Q119.46 22.15 119.65 22.99L119.79 23.14L119.69 23.04Q119.81 22.81 120.30 22.58L120.32 22.60L120.32 22.60Q121.08 23.55 121.20 24.69L121.18 24.68L121.28 24.78Q121.30 24.69 124.96 22.10L125.00 22.14L125.00 22.14Q124.64 19.42 123.19 18.74L123.21 18.76L123.17 18.71Q122.73 17.59 121.29 17.06L121.14 16.91L121.28 17.05Q118.90 16.15 115.70 16.15L115.75 16.20L115.77 16.22Q110.94 16.30 109.07 16.98L109.05 16.97L108.94 16.85Q107.62 17.32 107.50 19.00L107.56 19.05L107.61 19.10Q107.46 19.52 107.88 21.73L107.94 21.80L107.84 21.69Q108.63 25.56 110.80 27.81L110.75 27.77L110.76 27.77Q111.63 29.56 113.13 30.24L113.00 30.11L113.17 30.27Q114.60 30.89 118.53 32.45L118.40 32.33L118.53 32.49L119.21 32.88L119.14 32.84L119.09 32.79Q119.32 33.29 119.36 33.63L119.42 33.69L119.34 33.61Q119.39 37.05 115.47 37.20L115.43 37.16L115.48 37.21Q114.41 37.33 113.12 36.94L113.12 36.95L113.01 36.84Q112.82 36.15 112.82 35.43L112.76 35.37L112.82 35.43Q112.74 35.08 112.78 34.81L112.71 34.75L112.71 34.75Q112.46 35.14 111.66 35.63L111.63 35.61L111.56 35.54Q111.16 34.53 111.31 33.16L111.25 33.09L111.24 33.08Q109.14 34.26 107.54 35.67L107.63 35.75L107.53 35.66Q107.53 36.42 107.61 37.45L107.70 37.54L107.55 37.39Q108.00 39.25 109.30 40.01L109.32 40.04L109.37 40.09Q110.59 41.92 113.15 42.30L112.97 42.13L113.10 42.26Q114.62 42.45 117.56 42.64Z\"/></svg>",
        "svg": "http://127.0.0.1:8000/upload_files/captcha/captcha2024-06-04_10-20-19_mgnRkx0.png",
        "result": "bcus",
        "create_by": "tyler",
        "create_time": "2024-06-04T10:20:20.006194+08:00",
        "project": 1
    },

通过SVG源码转换的PNG,默认报错在django项目的MEDIA_ROOT目录下: 

我们需要通过nginx代理,将这些图片通过url的方式访问,生产环境中可以配置项目的URLS进行如下测试:

urlpatterns = [...]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

类似通过url访问静态文件:

  • 通过Nginx代理:

配置docker-compose:

在构建django容器时,定义卷名,指向SVG转PNG后文件保存的路径:

    django_app:
        ...

        volumes:
            - svg-volumes:/app/upload_files/captcha

    nginx:
        ...
        volumes:
            - svg-volumes:/usr/share/nginx/html/upload_files/captcha 

    volumes:
          svg-volumes:

在构建django和nginx容器时,我们挂载的卷名相同,这样做的目的是当django识别SVG源码转换为PNG图片,保存在/app/upload_files/captcha路径下的同时,也会同步到另外一个容器nginx的代理目录/usr/share/nginx/html/upload_files/captcha下,不同容器间实现文件同步。

配置nginx代理:在nginx的default.conf中新增:

    # 新增代理上传的文件
    location /upload_files/{
        alias /usr/share/nginx/html/upload_files/;
        try_files $uri @django;
    }

构建Nginx镜像的DockerFile:

FROM nginx:alpine
COPY ./nginx_docker/static/ /usr/share/nginx/html/static/
COPY ./django_app/upload_files/captcha/ /usr/share/nginx/html/upload_files/captcha/
COPY ./nginx_docker/dist/ /usr/share/nginx/html/dist/
COPY ./nginx_docker/conf.d /etc/nginx/conf.d
VOLUME /var/log/

EXPOSE 80 81

这里需要注意的是我们在部署这些容器时,是相对于DockerFile的上下文的,在docker-compose中的nginx服务中如下配置

        build:

            context: .

            dockerfile: ./nginx_docker/Dockerfile

根目录就不再是DockerFile所在目录,而是auto目录:我这里部署的目录结构为

这些配置好以后,部署项目代码测试:代理图片成功!

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值