Python自動化開發學習24-Django下(其他)

圖片驗證碼

頁面顯示圖片的兩種方式

下面的2個img標籤都可以返回圖片。

<img src="/static/imgs/test.gif" />
<img src="/getimg/" />

第一個直接就能獲取靜態文件,第二個現在並不能返回圖片,還有配合個處理函數:

# urls.py 文件,寫一個url的對應關係
    path('getImg/'.lower(), views.get_img),

# views.py 文件,對應的處理函數
def get_img(request):
    data = open('static/imgs/test.gif', 'rb').read()
    return HttpResponse(data)

上面的處理函數,這裏看到了HttpResponse的另外一個用法,之前都是返回的字符串。這裏返回的是二進制。
第一個方法必須要有一個文件路徑,img標籤直接去獲取這個文件。而第二個方法只需要返回文件的內容就可以了,甚至可以沒有這個文件。

生成驗證碼圖片

這裏需要依賴2個東西:Pillow模塊,字體文件(.ttf文件)
安裝Pillow模塊:

pip install Pillow

字體文件:windows系統的 C:\Windows\Fonts 目錄下有很多字體文件,可以複製一個合適的拿來用。
整個功能就是一個組件,這裏在項目目錄下創建一個utils文件,統一來存放用的的組件或者工具。把下面的生成驗證碼的函數以及用到的字體文件都放到utiles文件夾下。
下面是現成的可以生成驗證碼圖片的函數:

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小寫字母,去除可能干擾的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大寫字母
_numbers = ''.join(map(str, range(3, 10)))  # 數字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="utils/Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance=2):
    """
    @todo: 生成驗證碼圖片
    @param size: 圖片的大小,格式(寬,高),默認爲(120, 30)
    @param chars: 允許的字符集合,格式字符串
    @param img_type: 圖片保存的格式,默認爲GIF,可選的爲GIF,JPEG,TIFF,PNG
    @param mode: 圖片模式,默認爲RGB
    @param bg_color: 背景顏色,默認爲白色
    @param fg_color: 前景色,驗證碼字符顏色,默認爲藍色#0000FF
    @param font_size: 驗證碼字體大小
    @param font_type: 驗證碼字體,默認爲 ae_AlArabiya.ttf
    @param length: 驗證碼字符個數
    @param draw_lines: 是否劃干擾線
    @param n_lines: 干擾線的條數範圍,格式元組,默認爲(1, 2),只有draw_lines爲True時有效
    @param draw_points: 是否畫干擾點
    @param point_chance: 干擾點出現的概率,大小範圍[0, 100]
    @return: [0]: PIL Image實例
    @return: [1]: 驗證碼圖片中的字符串
    """

    width, height = size  # 寬高
    # 創建圖形
    img = Image.new(mode, size, bg_color)
    draw = ImageDraw.Draw(img)  # 創建畫筆

    def get_chars():
        """生成給定長度的字符串,返回列表格式"""
        return random.sample(chars, length)

    def create_lines():
        """繪製干擾線"""
        line_num = random.randint(*n_line)  # 干擾線條數

        for i in range(line_num):
            # 起始點
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            # 結束點
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        """繪製干擾點"""
        chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        """繪製驗證碼字符"""
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars)  # 每個字符前後以空格隔開

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                  strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 圖形扭曲參數
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params)  # 創建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 濾鏡,邊界加強(閾值更大)

    return img, strs

代碼說明
準備好字體文件,這裏也放到utils文件夾下,並在設置裏引入字體文件 font_type="utils/Monaco.ttf",
整個模塊只有一個函數create_validate_code()。另外內部還有幾個函數,但是都是在create_validate_code函數內部使用的。使用的時候只需要調用create_validate_code()方法,然後可以獲取到2個返回值,一次是圖片的對象和對應的驗證碼的字符串。

返回驗證碼圖片

重寫get_img處理函數,調用上面的模塊,返回圖片對象的二進制數據給前端的img標籤:

# views.py 文件

from io import BytesIO  # 字節的IO操作
from utils.check_code import create_validate_code
def get_img(request):
    img, code = create_validate_code()
    print(img, code)  # img是圖片對象,code是對應的字符串
    # file = open('img.png', 'wb')  # 打開文件
    # img.save(file, 'PNG')  # 傳入文件句柄,保存到文件,後面的參數是圖片的格式
    stream = BytesIO()  # 相當於打開了一個文件
    img.save(stream, 'PNG')  # 這裏就不需要生成文件了,而是放在內存裏
    # request.session['checkCode'] = code # 把驗證碼保存到session中
    return HttpResponse(stream.getvalue())

這裏還用到了BytesIO,因爲要在內存中直接做讀寫操作。數據讀寫不一定是文件,也可以在內存中讀寫。StringIO顧名思義就是在內存中讀寫str。StringIO操作的只能是str,如果要操作二進制數據,就需要使用BytesIO。StringIO和BytesIO是在內存中操作str和bytes的方法,使得和讀寫文件具有一致的接口。

點擊圖片刷新

目前需要刷新整個頁面才能刷新圖片,實際應用中需要點擊圖片,只刷新驗證碼圖片,頁面的其他內容不變化。
這裏需要給img標籤綁定一個onclick事件,通過這個事件給img標籤的src賦值。但是這裏會有一個問題,瀏覽器的機制是隻有src的值發生了變化纔會發起一個新的請求,這就要求src的值需要和之前的值不同,並且還不能改變請求的地址。下面是實現點擊刷新的方法:

<img src="/getimg/" />
<script>
    document.getElementsByTagName('img')[0].onclick = function () {
        // this.src = '/getimg/?';  // 指點點擊的第一次會變
        this.src = this.src + '?';
    };
</script>

這裏用了一個老師號稱機智的辦法。get請求中的url會用到問號(?),用來分隔請求地址和參數的。這裏每一次點擊都會在後面添加一個問號,這樣src的值變化了,但是請求的地址不變

富文本編輯框

常見的這類插件有:CKEditor、UEEditor、TinyEditor、KindEditor(下面要講的)。
講師的博客地址:http://www.cnblogs.com/wupeiqi/articles/6307554.html
KindEditor官網(中文):http://kindeditor.net/demo.php

安裝

首先去官網下載,然後把文件夾複製到static靜態文件目錄下。您可以根據需求刪除以下目錄後上傳到服務器:

  • asp - ASP程序
  • asp.net - ASP.NET程序
  • php - PHP程序
  • jsp - JSP程序
  • examples - 演示文件

上面這些都是各種語言的示例,所以可以不要。

基本用法

首先在再頁面上放一個textarea標籤。這個博客的編輯器裏絕對不能寫textarea標籤,所以代碼裏就故意寫錯了:

<div>
    <多行文本 id="editor_id" name="content" style="width:700px;height:300px;"></多行文本>
</div>
<script src="http://lib.sinaapp.com/js/jquery/1.12.4/jquery-1.12.4.min.js"></script>
<script src="/static/kindeditor/kindeditor-all-min.js"></script>
<script>
    $(function () {
        KindEditor.create('#editor_id', {})
    })
</script>

在有些瀏覽器上不設寬度和高度可能顯示有問題,所以最好設一下寬度和高度。寬度和高度可用inline樣式設置,也可用編輯器初始化參數設置。這裏先用了前者。
KindEditor.create()方法就是進行編輯器初始化的。第一個參數是一個CSS選擇器。第二個參數可以不寫,這裏寫了個空字典和不寫一樣。是對編輯器進行配置的。
下面是簡單的對高度和寬度進行了設置:

<script>
    $(function () {
        KindEditor.create('#editor_id', {
            width: '100%',  // 寬度,可以是百分比也可以是像素
            height: '600px',  // 高度,只能是像素
            minWidth: 400,  // 最小寬度
            minHeight: 200  // 最小高度
        })
    })
</script>

更多的參數設置還是看中文的官方文檔吧:http://kindeditor.net/docs/option.html

上傳文件

編輯器初始化的部分可以這麼寫,這裏用了items參數把別的按鈕都去掉了。主要是設置2個參數,一個是上傳文件的url。另一個可以在上傳的時候添加額外的參數,這裏要把csrf_token加上:

<script>
    $(function () {
        KindEditor.create('#editor_id', {
            items: ['image', 'insertfile'],  // 讓工具欄只顯示上傳圖片和上傳文件2個按鈕
            uploadJson: '/upload/',  // 指定上傳提交的url
            // allowImageUpload: false,  // 這個參數設爲false,就沒有本地上傳的分頁了。即不允許本地上傳
            extraFileUploadParams: {'csrfmiddlewaretoken': "{{ csrf_token }}"}  // 上傳時,添加別的參數一併上傳。
        })
    })
</script>

服務端收到請求後,要按下面的JSON格式給客戶端返回數據。官方文檔:http://kindeditor.net/docs/upload.html

# 成功時
{
    "error" : 0,
    "url" : "http://www.example.com/path/to/file.ext"
}
# 失敗時
{
    "error" : 1,
    "message" : "錯誤信息"
}

這裏可以進控制檯查看前端生成的html,使用的是iframe和form,標籤內部還有很多別的標籤就不貼了:

<iframe name="kindeditor_upload_iframe_1526735278206" style="display:none;"></iframe>
<form class="ke-upload-area ke-form" action="/upload/?dir=image" enctype="multipart/form-data" method="post" target="kindeditor_upload_iframe_1526735278206"></form>

服務器端要寫好對應的處理函數:

# urls.py 文件,url的對應關係
    path('upload/', views.upload),

# views.py文件,對應的處理函數
import json
def upload(request):
    print(request.GET.get('dir'))  # 打印收到的文件的上傳方式。image 或 file,另外還有flash等等
    print(request.FILES)  # 打印收到的文件對象
    ret = {'error': 0,  # 0是成功,1是有錯誤
           'url': '/static/imgs/test.gif',  # 返回圖片保存的url,用來給前端生成預覽的
           }
    return HttpResponse(json.dumps(ret))

這裏先不保存,只是把接收到的內容打印出來,返回一個表示上傳成功的信息。信息裏包含一個url,這個url是服務器端保存圖片的路徑,是用來給客戶端瀏覽器生成預覽的。這裏暫時沒保存圖片,先把服務器上已經有的一張圖片的url返回,讓客戶端可以生成一個預覽。

文件空間管理

這個編輯器還提供文件管理的功能,官方的叫法是瀏覽遠程服務器(FileManager)。需要設置下面的2個參數:

  • allowFileManager :true時顯示瀏覽遠程服務器按鈕。默認值: false
  • fileManagerJson :指定瀏覽遠程圖片的服務器端程序。和上傳文件的url的參數一樣,設置好url,然後去後端寫好處理函數

後端的處理函數就不展開了,不過講師的博客裏有,這裏就只留個鉤子吧。

MarkDown編輯框

我這裏試了下這個Editor.md :https://pandao.github.io/editor.md/index.html

安裝

首先去官網下載,然後把文件夾複製到static靜態文件目錄下。
在頁面裏寫上下面的代碼就可以使用默認的設置了:

<body>
<link rel="stylesheet" href="/static/editor.md-master/css/editormd.min.css" />
<div style="height:600px;">
    <div id="editormd"></div>
</div>
<script src="/static/editor.md-master/examples/js/jquery.min.js"></script>
<script src="/static/editor.md-master/editormd.min.js"></script>
<script type="text/javascript">
    $(function() {
        var editor = editormd("editormd", {
            path : "/static/editor.md-master/lib/" // Autoload modules mode, codemirror, marked... dependents libs path
        });

        /*
        // or
        var editor = editormd({
            id   : "editormd",
            path : "/static/editor.md-master/lib/"
        });
        */
    });
</script>
</body>

上面在外層的div是自己加上的,主要是設置一個高度,因爲默認的初始化的時候高度和寬度都是100%。所以設置一下高度的話也可以去掉自己的div。

設置

基本的初始化設置有下面這些,從源碼裏找到的:

        mode                 : "gfm",          //gfm or markdown
        name                 : "",             // Form element name
        value                : "",             // value for CodeMirror, if mode not gfm/markdown
        theme                : "",             // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty
        editorTheme          : "default",      // Editor area, this is CodeMirror theme at v1.5.0
        previewTheme         : "",             // Preview area theme, default empty
        markdown             : "",             // Markdown source code
        appendMarkdown       : "",             // if in init textarea value not empty, append markdown to textarea
        width                : "100%",
        height               : "100%",
        path                 : "./lib/",       // Dependents module file directory
        pluginPath           : "",             // If this empty, default use settings.path + "../plugins/"
        delay                : 300,            // Delay parse markdown to html, Uint : ms
        autoLoadModules      : true,           // Automatic load dependent module files
        watch                : true,
        placeholder          : "Enjoy Markdown! coding now...",
        gotoLine             : true,
        codeFold             : false,
        autoHeight           : false,
        autoFocus            : true,
        autoCloseTags        : true,
        searchReplace        : true,
        syncScrolling        : true,           // true | false | "single", default true
        readOnly             : false,
        tabSize              : 4,
        indentUnit           : 4,
        lineNumbers          : true,
        lineWrapping         : true,
        autoCloseBrackets    : true,
        showTrailingSpace    : true,
        matchBrackets        : true,
        indentWithTabs       : true,
        styleSelectedText    : true,
        matchWordHighlight   : true,           // options: true, false, "onselected"
        styleActiveLine      : true,           // Highlight the current line
        dialogLockScreen     : true,
        dialogShowMask       : true,
        dialogDraggable      : true,
        dialogMaskBgColor    : "#fff",
        dialogMaskOpacity    : 0.1,
        fontSize             : "13px",
        saveHTMLToTextarea   : false,
        disabledKeyMaps      : [],

這裏有一些詳細的說明:https://pandao.github.io/editor.md/examples/index.html
下載下來的examples文件夾裏也有一份一樣的。

自定義工具欄

官方文檔裏有的內容就不反覆說明了,初始化的時候給toolbarIcons這個參數賦值,可以是列表。也可以是字符串(只能是full, simple, mini),不過也可以自定義一個名字,然後用字符串引入:

<script>
    $(function () {
        editormd.toolbarModes.steed = ["undo", "redo", "|", "bold", "del", "italic", "quote"];
        var editor = editormd("editormd", {
            path : "/static/editor.md-master/lib/",
            toolbarIcons: 'steed',
        });
    })
</script>

還可以自己創建按鈕,偷懶不搞圖標的話,也可以直接使用文字,還能給自定義的按鈕添加事件:

    $(function () {
        editormd.toolbarModes.steed = ["undo", "redo", "|", "bold", "del", "italic", "quote",
        "||", "top", "submit", "help", "info"];
        var editor = editormd("editormd", {
            path : "/static/editor.md-master/lib/",
            toolbarIcons: 'steed',
            toolbarIconTexts: {top: "回到頂部", submit: '提交'},
            toolbarHandlers: {
                top: function () {
                    alert('回到頂部');
                }
            },
        });
    })

自己以的方法了提供了一下4個對象可以使用,具體還是看官方的例子把:

  • @param {Object}, cm: CodeMirror對象
  • @param {Object}, icon: 圖標按鈕jQuery元素對象
  • @param {Object}, cursor: CodeMirror的光標對象,可獲取光標所在行和位置
  • @param {String}, selection: 編輯器選中的文本

獲取文本

這裏主要講下面3個方法

  • .getMarkdown(): 獲取MarkDown文本
  • .getHTML(): 獲取HTML文本,需要開啓 saveHTMLToTextarea: true,
  • .setMarkdown(md):填入MarkDown文本

下面的例子自定義了2個按鈕,分別能獲取到文本的MarkDown格式以及HTML格式的內容:

{% load static %}
<script>
    $(function () {
        editormd.toolbarModes.steed = ["undo", "redo", "|", "bold", "del", "italic", "quote",
        "||", "get_markdown", "get_html", "top", "help", "info"];
        var editor = editormd("editormd", {
            path : "/static/editor.md-master/lib/",
            toolbarIcons: 'steed',
            saveHTMLToTextarea: true,
            toolbarIconTexts: {get_markdown: "Get MarkDown", get_html: 'Get HTML',  top: "TOP"},
            toolbarHandlers: {
                get_markdown: function() {
                    alert(editor.getMarkdown());
                },
                get_html: function() {
                    alert(editor.getHTML());
        }
            },
        });
        $.get("{% static "editor.md-master/examples/test.md" %}", function(md){
            editor.setMarkdown(md);
        })
    })
</script>

另外上面的例子的最後一個 $.get() 方法是獲取到文本後,在回調函數裏調用editor.setMarkdown(md);填寫到文本框內。
獲取MarkDown的方法獲取到文本後,就可以實現上傳到服務器的功能了。
獲取HTML的方法可以獲取到HTML的文本,不過貌似沒什麼用,要把MarkDown文本直接展示在HTML上有另外的轉化方法。
自動填入文本框,則可以實現文檔的修改的功能。

顯示MarkDown

之前將編輯器的內容上傳並保存到數據庫,如果上傳的內容是 .getMarkdown() 獲取的Markdown文本。那麼現在要做一個展示的頁面,將數據庫中的內容顯示到頁面上,並且展示的應該是html格式:

{% load static %}
<div class="container">
    <div id="doc-content"><textarea style="display: none">{{ article_obj.articledetail.content }}</textarea></div>
</div>
<script src="{% static "editor.md-master/editormd.js" %}"></script>
<script src="{% static "editor.md-master/lib/marked.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/prettify.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/raphael.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/underscore.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/sequence-diagram.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/flowchart.min.js" %}"></script>
<script src="{% static "editor.md-master/lib/jquery.flowchart.min.js" %}"></script>
<script>
    $(function () {
        editormd.markdownToHTML('doc-content')
    })
</script>

上面不知道爲什麼要引入那麼多js,不過一個都不用試下來是無法顯示的。官網的例子裏也引用那麼多js,這裏就全部引用了。
另外,這裏的markdownToHTML沒有加參數,使用的都是默認設置。如果需要調整,那麼第二個參數傳入一個字典進行設置:

<script type="text/javascript">
    $(function() {
        editormd.markdownToHTML("doc-content", {
            htmlDecode : "style,script,iframe",
            emoji : true,
            taskList : true,
            tex : true, // 默認不解析
            flowChart : true, // 默認不解析
            sequenceDiagram : true, // 默認不解析
            codeFold : true
        });
    });
</script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章