Upload photo to qiniu

Brightcells 8 jaren geleden
bovenliggende
commit
e38c2defaf
11 gewijzigde bestanden met toevoegingen van 224 en 32 verwijderingen
  1. 1 1
      group/lensman_views.py
  2. 12 11
      group/models.py
  3. 1 1
      group/tourguidegroup_views.py
  4. 6 5
      group/views.py
  5. 15 0
      pai2/settings.py
  6. 5 5
      photo/models.py
  7. 1 1
      photo/views.py
  8. 5 4
      requirements.txt
  9. 43 0
      utils/qiniucdn.py
  10. 135 0
      utils/storage_qiniu_utils.py
  11. 0 4
      utils/url_utils.py

+ 1 - 1
group/lensman_views.py

@@ -26,7 +26,7 @@ from utils.redis.rlock import upload_lock
26 26
 from utils.redis.rorder import set_lensman_order_record
27 27
 from utils.redis.rprice import get_lensman_price_fixed, set_lensman_price_fixed
28 28
 from utils.redis.rprofile import set_profile_info
29
-from utils.storage_utils import file_save
29
+from utils.storage_qiniu_utils import file_save
30 30
 
31 31
 
32 32
 @logit

+ 12 - 11
group/models.py

@@ -6,10 +6,11 @@ from shortuuidfield import ShortUUIDField
6 6
 from TimeConvert import TimeConvert as tc
7 7
 
8 8
 from pai2.basemodels import CreateUpdateMixin
9
+from utils.qiniucdn import qiniu_file_url
9 10
 from utils.redis.rgroup import get_group_photo_thumbup_flag
10 11
 from utils.redis.rorder import get_lensman_order_record
11 12
 from utils.time_utils import origin_expired_stamps
12
-from utils.url_utils import img_url, share_url
13
+from utils.url_utils import share_url
13 14
 
14 15
 
15 16
 class GroupInfo(CreateUpdateMixin):
@@ -58,19 +59,19 @@ class GroupInfo(CreateUpdateMixin):
58 59
 
59 60
     @property
60 61
     def group_avatar_url(self):
61
-        return img_url(self.group_avatar)
62
+        return qiniu_file_url(self.group_avatar, bucket='photo')
62 63
 
63 64
     @property
64 65
     def group_attentions_url(self):
65
-        return img_url(self.attentions_path)
66
+        return qiniu_file_url(self.attentions_path, bucket='photo')
66 67
 
67 68
     @property
68 69
     def group_schedules_url(self):
69
-        return img_url(self.schedules_path)
70
+        return qiniu_file_url(self.schedules_path, bucket='photo')
70 71
 
71 72
     @property
72 73
     def gather_screenshot_url(self):
73
-        return img_url(self.gather_screenshot)
74
+        return qiniu_file_url(self.gather_screenshot, bucket='photo')
74 75
 
75 76
     @property
76 77
     def group_photo_num(self):
@@ -260,15 +261,15 @@ class GroupPhotoInfo(CreateUpdateMixin):
260 261
 
261 262
     @property
262 263
     def photo_url(self):
263
-        return img_url(self.photo_path)
264
+        return qiniu_file_url(self.photo_path, bucket='photo')
264 265
 
265 266
     @property
266 267
     def photo_thumbnail_url(self):
267
-        return img_url(self.photo_thumbnail_path)
268
+        return qiniu_file_url(self.photo_thumbnail_path, bucket='thumbnail')
268 269
 
269 270
     @property
270 271
     def photo_thumbnail2_url(self):
271
-        return img_url(self.photo_thumbnail2_path)
272
+        return qiniu_file_url(self.photo_thumbnail2_path, bucket='thumbnail2')
272 273
 
273 274
     @property
274 275
     def photo_share_url(self):
@@ -342,15 +343,15 @@ class GroupPhotoOrderInfo(CreateUpdateMixin):
342 343
 
343 344
     @property
344 345
     def m_photo_url(self):
345
-        return img_url(self.m_photo_path)
346
+        return qiniu_file_url(self.m_photo_path, bucket='photo')
346 347
 
347 348
     @property
348 349
     def l_photo_url(self):
349
-        return img_url(self.l_photo_path)
350
+        return qiniu_file_url(self.l_photo_path, bucket='prettify')
350 351
 
351 352
     @property
352 353
     def r_photo_url(self):
353
-        return img_url(self.r_photo_path)
354
+        return qiniu_file_url(self.r_photo_path, bucket='original')
354 355
 
355 356
     @property
356 357
     def porder_info(self):

+ 1 - 1
group/tourguidegroup_views.py

@@ -21,7 +21,7 @@ from utils.redis.rgroup import get_group_info, get_group_users_info, set_group_i
21 21
 from utils.redis.rkeys import TOUR_GUIDE_GROUP_CUR_GATHER_INFO, TOUR_GUIDE_GROUP_CUR_SESSION
22 22
 from utils.redis.rtourguide import get_tour_guide_own_group, set_tour_guide_own_group
23 23
 from utils.redis.rtouruser import get_tour_user_belong_group
24
-from utils.storage_utils import file_save
24
+from utils.storage_qiniu_utils import file_save
25 25
 
26 26
 
27 27
 @logit

+ 6 - 5
group/views.py

@@ -17,6 +17,7 @@ from message.models import UserMessageInfo
17 17
 from utils.error.errno_utils import GroupPhotoStatusCode, GroupStatusCode, GroupUserStatusCode, UserStatusCode
18 18
 from utils.error.response_utils import response
19 19
 from utils.group_photo_utils import get_current_photos
20
+from utils.qiniucdn import qiniu_file_url
20 21
 from utils.redis.connect import r
21 22
 from utils.redis.rgroup import (del_group_photo_thumbup_flag, get_group_info, get_group_photo_comment_list,
22 23
                                 get_group_photo_data, get_group_photo_thumbup_flag, get_group_photo_thumbup_list,
@@ -28,9 +29,9 @@ from utils.redis.rlock import upload_lock
28 29
 from utils.redis.rorder import get_lensman_order_record
29 30
 from utils.redis.rprice import get_lensman_price_fixed
30 31
 from utils.sql.raw import PAI2_HOME_API
31
-from utils.storage_utils import file_save
32
+from utils.storage_qiniu_utils import file_save
32 33
 from utils.time_utils import origin_expired_stamps
33
-from utils.url_utils import img_url, share_url
34
+from utils.url_utils import share_url
34 35
 
35 36
 
36 37
 @logit
@@ -632,13 +633,13 @@ def pai2_home_api(request):
632 633
         'group_avatar': row[3],
633 634
         'group_from': row[4],
634 635
         'photo_id': row[5],
635
-        'photo_url': img_url(row[6]),
636
+        'photo_url': qiniu_file_url(row[6], bucket='photo'),
636 637
         'photo_w': row[7],
637 638
         'photo_h': row[8],
638
-        'photo_thumbnail_url': img_url(row[9]),
639
+        'photo_thumbnail_url': qiniu_file_url(row[9], bucket='thumbnail'),
639 640
         'photo_thumbnail_w': row[10],
640 641
         'photo_thumbnail_h': row[11],
641
-        'photo_thumbnail2_url': img_url(row[12]),
642
+        'photo_thumbnail2_url': qiniu_file_url(row[12], bucket='thumbnail2'),
642 643
         'photo_thumbnail2_w': row[13],
643 644
         'photo_thumbnail2_h': row[14],
644 645
         'photo_share_url': share_url(row[5]),  # Warning: Index of This Line is 5

+ 15 - 0
pai2/settings.py

@@ -225,6 +225,21 @@ WECHAT_OAUTH2_RETRY_REDIRECT_URI = 'https://api.pai.ai/wx_oauth2?redirect_url={}
225 225
 
226 226
 WECHAT_OAUTH2_REDIRECT_URL = 'https://api.pai.ai/wx_oauth2?redirect_url={}'
227 227
 
228
+# 七牛设置
229
+QINIU = {
230
+    'access_key': 'yCE3xWXduLTERkx_vSNVAIHNcg1pje6EwygiRPjP',
231
+    'secret_key': '05sCekniLCgM6-d_PxrH8sFjvEOsx3ev-FgS7R-k',
232
+    'bucket_default': 'photo',
233
+    'buckets': {
234
+        'original': 'http://orf3sfb8s.bkt.clouddn.com',
235
+        'photo': 'http://orf3lnlmb.bkt.clouddn.com',
236
+        'prettify': 'http://orzfu8zxw.bkt.clouddn.com',
237
+        'thumbnail': 'http://orf3ahvt6.bkt.clouddn.com',
238
+        'thumbnail2': 'http://orf3muf5n.bkt.clouddn.com',
239
+        'watermark': 'http://orf3qne9f.bkt.clouddn.com',
240
+    }
241
+}
242
+
228 243
 # 图片设置
229 244
 FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880  # InMemoryUploadedFile 文件最大值
230 245
 FILE_UPLOAD_PERMISSIONS = 0o644  # TemporaryUploadedFile 文件权限设置

+ 5 - 5
photo/models.py

@@ -4,7 +4,7 @@ from django.db import models
4 4
 from django.utils.translation import ugettext_lazy as _
5 5
 
6 6
 from pai2.basemodels import CreateUpdateMixin
7
-from utils.url_utils import img_url
7
+from utils.qiniucdn import qiniu_file_url
8 8
 
9 9
 
10 10
 class UUIDInfo(CreateUpdateMixin):
@@ -78,19 +78,19 @@ class PhotosInfo(CreateUpdateMixin):
78 78
 
79 79
     @property
80 80
     def p_photo_url(self):
81
-        return img_url(self.p_photo_path)
81
+        return qiniu_file_url(self.p_photo_path, bucket='watermark')
82 82
 
83 83
     @property
84 84
     def m_photo_url(self):
85
-        return img_url(self.m_photo_path)
85
+        return qiniu_file_url(self.m_photo_path, bucket='photo')
86 86
 
87 87
     @property
88 88
     def l_photo_url(self):
89
-        return img_url(self.l_photo_path)
89
+        return qiniu_file_url(self.l_photo_path, bucket='prettify')
90 90
 
91 91
     @property
92 92
     def r_photo_url(self):
93
-        return img_url(self.r_photo_path)
93
+        return qiniu_file_url(self.r_photo_path, bucket='original')
94 94
 
95 95
     @property
96 96
     def data(self):

+ 1 - 1
photo/views.py

@@ -21,7 +21,7 @@ from utils.redis.rkeys import (GROUP_LAST_PHOTO_PK, GROUP_USERS_DELETED_SET, GRO
21 21
                                GROUP_USERS_QUIT_SET, GROUP_USERS_REFUSED_SET, UUID_LIST)
22 22
 from utils.redis.rprice import get_lensman_price_fixed
23 23
 from utils.redis.ruuid import generate_uuids, update_uuids
24
-from utils.storage_utils import file_save
24
+from utils.storage_qiniu_utils import file_save
25 25
 
26 26
 
27 27
 @logit

+ 5 - 4
requirements.txt

@@ -18,21 +18,22 @@ django-paginator2==1.0.3
18 18
 django-rlog==1.0.7
19 19
 django-shortuuidfield==0.1.3
20 20
 django-six==1.0.2
21
-djangorestframework==3.5.3
22
-furl==0.5.7
21
+djangorestframework==3.6.3
22
+furl==1.0.0
23 23
 hiredis==0.2.0
24 24
 isoweek==1.3.3
25
-jsonfield==2.0.1
25
+jsonfield==2.0.2
26 26
 mock==2.0.0
27 27
 pep8==1.7.0
28 28
 pysnippets==1.0.4
29 29
 pywe-miniapp==1.0.0
30 30
 pywe-oauth==1.0.3
31 31
 pywe-response==1.0.1
32
+qiniu==7.1.4
32 33
 redis-extensions==1.0.50
33 34
 requests==2.12.4
34 35
 rlog==0.2
35 36
 shortuuid==0.5.0
36
-uWSGI==2.0.14
37
+uWSGI==2.0.15
37 38
 versions==0.10.0
38 39
 wechatpy==1.2.8

+ 43 - 0
utils/qiniucdn.py

@@ -0,0 +1,43 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import qiniu
4
+from django.conf import settings
5
+
6
+
7
+QINIU = settings.QINIU
8
+auth = qiniu.Auth(QINIU['access_key'], QINIU['secret_key'])
9
+
10
+
11
+def upload(data, key=None, mime_type='application/octet-stream', bucket=QINIU['bucket_default']):
12
+    if not data:
13
+        return ''
14
+    token = auth.upload_token(bucket, key=key)
15
+    ret, _ = qiniu.put_data(token, key, data, mime_type=mime_type)
16
+    return ret.get('key')
17
+
18
+
19
+def upload_file_admin(obj, key=None, mime_type='application/octet-stream', bucket=QINIU['bucket_default']):
20
+    # Django Admin Upload
21
+    if not obj.image:
22
+        return ''
23
+    return upload(obj.image.read(), key=key, mime_type=mime_type, bucket=bucket)
24
+
25
+
26
+def upload_file_req(photo, key=None, mime_type='application/octet-stream', bucket=QINIU['bucket_default']):
27
+    # photo = request.FILES.get('photo', '')
28
+    # <InMemoryUploadedFile: photo.png (image/png)>
29
+    if not photo:
30
+        return ''
31
+    return upload(photo.read(), key=key, mime_type=mime_type, bucket=bucket)
32
+
33
+
34
+def upload_file_path(path, key=None, mime_type='application/octet-stream', bucket=QINIU['bucket_default']):
35
+    if not path:
36
+        return ''
37
+    token = auth.upload_token(bucket, key=key)
38
+    ret, _ = qiniu.put_file(token, key, path, mime_type=mime_type)
39
+    return ret.get('key')
40
+
41
+
42
+def qiniu_file_url(key, bucket):
43
+    return '{}/{}'.format(QINIU['buckets'][bucket], key)

+ 135 - 0
utils/storage_qiniu_utils.py

@@ -0,0 +1,135 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+import shortuuid
6
+from django.conf import settings
7
+from django.core.files.storage import default_storage
8
+from django.db import transaction
9
+from filemd5 import calculate_md5
10
+
11
+from photo.models import PhotoUUIDInfo
12
+from utils.qiniucdn import upload_file_path
13
+from utils.thumbnail_utils import make_thumbnail
14
+from utils.watermark_utils import watermark_wrap
15
+
16
+
17
+class DotDict(dict):
18
+    """ dot.notation access to dictionary attributes """
19
+    def __getattr__(self, attr):
20
+        return self.get(attr)
21
+    __setattr__ = dict.__setitem__
22
+    __delattr__ = dict.__delitem__
23
+
24
+
25
+def file_abspath(file_path=None):
26
+    return os.path.join(settings.MEDIA_ROOT, file_path).replace('\\', '/')
27
+
28
+
29
+@transaction.atomic
30
+def file_save(file_=None, file_path=None, prefix='img', ext='.jpeg', watermark=False, thumbnail=False):
31
+    photo_path, photo_watermark_path, photo_thumbnail_path, photo_thumbnail2_path = '', '', '', ''
32
+
33
+    # Photo
34
+    file_ = file_ or default_storage.open(file_path)
35
+
36
+    # Ext
37
+    ext = os.path.splitext(file_.name)[-1] or ext
38
+
39
+    # Photo MD5
40
+    photo_md5 = calculate_md5(file_)
41
+
42
+    # Photo UUID Get or Create
43
+    photo, created = PhotoUUIDInfo.objects.select_for_update().get_or_create(photo_md5=photo_md5)
44
+
45
+    # 无水印
46
+    if not photo.photo_path:
47
+        photo_path = '{}/{}{}'.format(prefix, shortuuid.uuid(), ext)
48
+        if default_storage.exists(photo_path):
49
+            default_storage.delete(photo_path)
50
+        default_storage.save(photo_path, file_)
51
+        photo_path_qiniu = upload_file_path(file_abspath(photo_path), bucket='photo')
52
+        photo.photo_path = photo_path_qiniu
53
+        photo.save()
54
+    else:
55
+        if ((watermark and not photo.photo_watermark_path) or (thumbnail and not (photo.photo_thumbnail_path and photo.photo_thumbnail2_path))) and (not default_storage.exists(photo.photo_path)):
56
+            default_storage.save(photo.photo_path, file_)
57
+
58
+    # 有水印
59
+    if watermark:
60
+        if not photo.photo_watermark_path:
61
+            if settings.WATERMARK_OR_NOT:
62
+                photo_watermark_path = 'photo/{}{}'.format(shortuuid.uuid(), ext)
63
+                watermark_wrap(
64
+                    file_abspath(photo_path),
65
+                    settings.WATERMARK_LOGO_PATH,
66
+                    file_abspath(photo_watermark_path)
67
+                )
68
+                photo_watermark_path_qiniu = upload_file_path(file_abspath(photo_watermark_path), bucket='watermark')
69
+                photo.photo_watermark_path = photo_watermark_path_qiniu
70
+            else:
71
+                photo.photo_watermark_path = photo_path_qiniu
72
+            photo.save()
73
+
74
+    # 缩略图
75
+    if thumbnail:
76
+        if not photo.photo_thumbnail_path:
77
+            # 双列: 540, 40-50K
78
+            photo_thumbnail_path = photo_path.replace('.', '_thumbnail.')
79
+            photo_w, photo_h, photo_thumbnail_w, photo_thumbnail_h = make_thumbnail(
80
+                file_abspath(photo_path),
81
+                file_abspath(photo_thumbnail_path),
82
+                settings.THUMBNAIL_MAX_WIDTH
83
+            )
84
+            photo_thumbnail_path_qiniu = upload_file_path(file_abspath(photo_thumbnail_path), bucket='thumbnail')
85
+            photo.photo_w = photo_w
86
+            photo.photo_h = photo_h
87
+            photo.photo_thumbnail_path = photo_thumbnail_path_qiniu
88
+            photo.photo_thumbnail_w = photo_thumbnail_w
89
+            photo.photo_thumbnail_h = photo_thumbnail_h
90
+        if not photo.photo_thumbnail2_path:
91
+            # 单列: 1080, xx-100K
92
+            photo_thumbnail2_path = photo_path.replace('.', '_thumbnail2.')
93
+            photo_w, photo_h, photo_thumbnail2_w, photo_thumbnail2_h = make_thumbnail(
94
+                file_abspath(photo_path),
95
+                file_abspath(photo_thumbnail2_path),
96
+                settings.THUMBNAIL_MAX_WIDTH2
97
+            )
98
+            if watermark and settings.WATERMARK_OR_NOT:
99
+                watermark_wrap(
100
+                    file_abspath(photo_thumbnail2_path),
101
+                    settings.WATERMARK_LOGO_PATH,
102
+                    file_abspath(photo_thumbnail2_path)
103
+                )
104
+            photo_thumbnail2_path_qiniu = upload_file_path(file_abspath(photo_thumbnail2_path), bucket='thumbnail2')
105
+            photo.photo_w = photo_w
106
+            photo.photo_h = photo_h
107
+            photo.photo_thumbnail2_path = photo_thumbnail2_path_qiniu
108
+            photo.photo_thumbnail2_w = photo_thumbnail2_w
109
+            photo.photo_thumbnail2_h = photo_thumbnail2_h
110
+        photo.save()
111
+
112
+    # 本地删除
113
+    for path in [photo_path, photo_watermark_path, photo_thumbnail_path, photo_thumbnail2_path]:
114
+        if path and default_storage.exists(path):
115
+            default_storage.delete(path)
116
+
117
+    return DotDict({
118
+        'ext': ext,
119
+
120
+        'photo_md5': photo_md5,
121
+
122
+        'photo_path': photo.photo_path,
123
+        'photo_w': photo.photo_w,
124
+        'photo_h': photo.photo_h,
125
+
126
+        'photo_watermark_path': photo.photo_watermark_path,
127
+
128
+        'photo_thumbnail_path': photo.photo_thumbnail_path,
129
+        'photo_thumbnail_w': photo.photo_thumbnail_w,
130
+        'photo_thumbnail_h': photo.photo_thumbnail_h,
131
+
132
+        'photo_thumbnail2_path': photo.photo_thumbnail2_path,
133
+        'photo_thumbnail2_w': photo.photo_thumbnail2_w,
134
+        'photo_thumbnail2_h': photo.photo_thumbnail2_h,
135
+    })

+ 0 - 4
utils/url_utils.py

@@ -3,10 +3,6 @@
3 3
 from django.conf import settings
4 4
 
5 5
 
6
-def img_url(img_path):
7
-    return '{}/{}'.format(settings.IMG_DOMAIN, img_path) if img_path else ''
8
-
9
-
10 6
 def share_url(photo_id):
11 7
     return '{}/gp/{}'.format(settings.DOMAIN, photo_id) if photo_id else ''
12 8