Сегодня проекты, особенно на начальном этапе, строятся из готовых блоков. Например, умный
Насколько просто сейчас сделать такой сервис, как
Пример будут написан на Питоне. Во-первых, потому что Питон я знаю лучше всего, во-вторых библиотека pyuploadcare обновляется в первую очередь. На самом деле, для Uploadcare есть библиотеки под разные языки, и все они в open source. Если в нужном вам модуле отсутствует какая-то функциональность, можно дождаться, когда она появится, или дописать самому.
Начнем с создания нового проекта на Django:
$ pip install django pyuploadcare==0.19
$ django-admin.py startproject upload_test
$ cd upload_test/ && chmod u+x ./manage.py
$ ./manage.py startapp imageshare
$ ./manage.py syncdb --noinput
В settings.py, помимо привычных параметров подключения к базе данных и INSTALLED_APPS, нужно указать публичный и приватный ключ:
UPLOADCARE = {
'pub_key': 'demopublickey',
'secret': 'demoprivatekey',
}
Для демонстрации я буду использовать демонстрационный аккаунт, что само по себе уже кажется логичным. Единственное ограничение этого аккаунта в том, что файлы через какое-то время удаляются сами.
Проект будет совсем небольшой: на главной странице будет форма для загрузки. После её отправки идентификатор картинки будет сохраняться в базу. Для этого вполне хватит такой модели:
import string
import random
from pyuploadcare.dj import ImageField
from django.db import models
class Image(models.Model):
slug = models.SlugField(max_length=10, primary_key=True, blank=True)
image = ImageField(manual_crop="")
def save(self, *args, **kwargs):
if not self.slug:
self.slug = ''.join(random.sample(string.ascii_lowercase, 6))
super(Image, self).save(*args, **kwargs)
Как можно заметить, ImageField тут не джанговский, а из пакета pyuploadcare. Я указал только одну настройку: она позволит пользователю самому выбрать область изображения, которую он хочет загрузить. В методе save() генерируется slug для короткой ссылки.
Теперь прекрасное: вьюшка для главной страницы, сохраняющая картинку, и вьюшка, позволяющая её смотреть:
from django.views.generic import DetailView
from django.views.generic.edit import CreateView
from .models import Image
class UploadView(CreateView):
model = Image
class ImageView(DetailView):
model = Image
Класс, по 2 строчки. Просто Django — тоже очень качественный блок для ваших проектов. Чтобы форма отображалась, нужен небольшой шаблон, который будет её выводить. Ничего особенного, но нужно указать публичный ключ для виджета и не забыть поставить в тег документа {{ form.media }}. Многие забывают.
{% extends "base.html" %}
{% block head %}
<script>
UPLOADCARE_PUBLIC_KEY = 'demopublickey';
</script>
{{ form.media }}
{% endblock %}
{% block body %}
<div class="center">
<form action="." method="post">
<p>Please, select an image:</p>
<p>{{ form.image }}</p>
<p><input type="submit" value="send"></p>
{{ form.errors }}
</form>
</div>
{% endblock %}
Запускаем.
Виджет с выбором файлов появился на странице. Но вот сохранение не работает, Джанга ругается: «No URL to redirect to». Оно и понятно, нужно где-то указать, как получить полную ссылку на картинку. Добавим еще один метод к модели.
@models.permalink
def get_absolute_url(self):
return 'detail', (), {'pk': self.pk}
Осталось написать шаблон полного вывода и можно сказать, что цель достигнута.
{% block body %}
<img src="{{ image.image }}">
{% endblock %}
Ребята из моего инстаграма передают привет.
Внимательный читатель заметит, что на все про все ушло максимум минут 15. Wtf, чем же нам занять еще четверть часа?
Можно улучшить страницу загрузки. В нынешнем виде пользователю приходится делать два лишних клика: для открытия виджета и для отправки формы. Можно их убрать. Для этого нужно воспользоваться javascript api виджета:
<script>
(function() {
uploadcare.start();
var widget = uploadcare.Widget('#id_image');
widget.openDialog();
widget.onChange(function(file) {
if (file) {
var form = document.getElementById('upload-form');
form.submit();
form.style.display = 'none';
}
});
})();
</script>
Тут мы инициализируем виджет с помощью метода start(), не дожидаясь загрузки страницы, после чего открываем диалог, не дожидаясь клика пользователя. Ну а если был загружен файл, отправляем форму.
Что еще? Можно сделать немного информативнее страничку просмотра картинки: показать превьюшку вместо полной картинки и вывести немного информации.
{% block body %}
<h2>Uploaded Image</h2>
<a href="{{ image.image.cdn_url }}">
<img align="left" src="{{ image.image.cdn_url }}-/stretch/off/-/resize/260x/"></a>
<div class="float-info">
<p>
<b>Filename</b>: {{ image.image.info.filename }}<br>
<b>Uploaded</b>: {{ image.image.info.datetime_uploaded|slice:":10" }}<br>
<b>Original size</b>: {{ image.image.info.size|filesizeformat }}<br>
</p>
<p><a href="{{ image.image.cdn_url }}">Full link</a></p>
</div>
<br clear="left">
<p><a href="{% url 'index' %}">Upload another image</a></p>
{% endblock %}
Превьюшка нужного размера получается с помощью указания опций непосредственно в url картинки. Информация получается через метод info. К сожалению, datetime_uploaded передается в виде строки, поэтому пришлось схитрить — вырезать первые 10 символов. По-хорошему нужно было её парсить. Надеюсь, до десятитысячного года кто-нибудь исправит :)
Еще одна мелочь, которую можно исправить — правильно обрабатывать ситуацию, когда картинка была удалена. Правильно обрабатывать — значит отдавать ошибку 404 вместо 500. Лучше всего это делать при получении объекта из базы: запрашивать информацию о файле, и, если в ней есть признак того, что файл удален, удалять хранящуюся у нас ссылку. Кроме того, если файл удален достаточно давно, api может вовсе ничего не вернуть. Нужна обработка и такого случая.
class ImageView(DetailView):
model = Image
def get_object(self):
object = super(ImageView, self).get_object()
try:
if object.image.is_removed:
raise ValueError('File was deleted.')
except (InvalidRequestError, ValueError):
object.delete()
raise Http404
return object
Теперь, пожалуй, можно остановиться. Осталось задействовать последний блок: бесплатный до определенной нагрузки
Автор: homm