Djangoでcanvasに描いたものを画像ファイルとして保存する

題名にもあるとおり、ドンピシャな記事がなかったので記事にしておこうと思います。

要は、DjangoのRestFrameWorkでAPIにてcanvasで描かれたものを画像データとして保存するというものになります。

まずは、HTMLですがこちらは単純にcanvasを使う形ですね。

<canvas id="canvas" width="1024"></canvas>

で、ここで書かれたデータを取得するのは、

const canvasImage = canvas[orbiter.getClientID()].toDataURL("image/png")

こんな感じで、toDataURLというものを使用することでcanvasのデータを取得することができます。

しかし、これはbase64なのでこのままAPIで送ってしまうと例えばDjangoのModelでカラムに画像のパスだけを保存しておくという場合には使用することができません。それはさておき、APIを飛ばす側ですが、jQueryで実装すると下記のような形。

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
                beforeSend: function (xhr, settings) {
                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    }
                }
            });


let fd = new FormData();
fd.append("filepath",canvasImageで取得したcanvasのURL);

$.ajax({
    'url': 'アップロード先のAPIのURI',
    'type': 'POST',
    'data': fd,
    'processData': false,
    'contentType': false,
    'cache': false,
}).done(response => {
    console.log('成功');
}).fail(function (data) {
    console.log('失敗');
});

 

DjangoでPOSTをする場合、CSRFTOKENがないとデータを投げれないので最初の部分ではその設定をしています。

なお、ajaxSetupはjQueryのslimなどでは動かないので注意が必要です。

 

で、肝心のDjango側を紹介しますと(モジュールのimport系は省略します)、

models.py

class CanvasImages(models.Model):
    file_path = models.ImageField(verbose_name='file upload', upload_to='lesson/canvas/')

こんな感じにしています。ImageFieldなのでBloBではなく画像のファイルパスをカラムに設定する一般的なものかと思います。

urls.py

router = routers.DefaultRouter()
router.register(r'upload_file', ImagefileuploadViewset)

urlpatterns = [
    path('', include(router.urls)),
]

RestFrameWorkの純正な書き方にしたがっています。

serializers.py

class ImagefilesCanvasSerializer(serializers.ModelSerializer, serializers.ImageField):
    class Meta:
        model = CanvasImages
        fields = ['file_path']

ModelSerializerを使用しています。

views.py

class ImagefileuploadViewset(viewsets.ModelViewSet):
    queryset = CanvasImages.objects.all()
    serializer_class = ImagefilesCanvasSerializer

    def create(self, request, *args, **kwargs):
        data = request.data.get('filepath')
        if data:
            data += "=" * ((4 - len(data) % 4) % 4)
            format, imgstr = data.split(';base64,')
            ext = format.split('/')[-1]
            canvas = CanvasImages()
            canvas.file_path = ContentFile(base64.b64decode(imgstr), name=str(uuid.uuid4()) + '.' + ext)
            canvas.save()
        return Response(data, status=status.HTTP_201_CREATED)

 

こんな感じになっています。createメソッドを拡張する形にはなっていますが、saveを行っています。

情報として少なかったのは、

data += “=” * ((4 – len(data) % 4) % 4)
format, imgstr = data.split(‘;base64,’)

ここら辺ですね。

実際にはファイル名にはuuidを用いて保存を行っています。

 

何かの参考になれば幸いです。

 

 

 

 

 

 

コメントを残す