oss_upload.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import time
  2. from io import BytesIO
  3. from pathlib import Path
  4. import oss2
  5. import uuid
  6. try:
  7. from PIL import Image
  8. except ImportError as e:
  9. raise ImportError("请先安装 Pillow: pip install Pillow") from e
  10. OSS_ACCESS_KEY_ID = 'LTAI5tDwjfteBvivYN41r8sJ'
  11. OSS_ACCESS_KEY_SECRET = 'yowuOGi2nYYnrqGpO3qcz94C4brcPp'
  12. OSS_ENDPOINT = "oss-cn-shenzhen.aliyuncs.com"
  13. OSS_BUCKET_NAME = "zhijiayun-jiansuo"
  14. # 目标体积:约 60~100 KB(JPEG)
  15. TARGET_MIN_BYTES = 60 * 1024
  16. TARGET_MAX_BYTES = 100 * 1024
  17. try:
  18. _RESAMPLE = Image.Resampling.LANCZOS
  19. except AttributeError:
  20. _RESAMPLE = Image.LANCZOS
  21. def _jpeg_bytes(img, quality):
  22. buf = BytesIO()
  23. img.save(buf, format="JPEG", quality=int(quality), optimize=True)
  24. return buf.getvalue()
  25. def _prepare_rgb(img):
  26. if img.mode in ("RGBA", "LA"):
  27. background = Image.new("RGB", img.size, (255, 255, 255))
  28. background.paste(img, mask=img.split()[-1])
  29. return background
  30. if img.mode == "P":
  31. img = img.convert("RGBA")
  32. background = Image.new("RGB", img.size, (255, 255, 255))
  33. background.paste(img, mask=img.split()[-1])
  34. return background
  35. return img.convert("RGB")
  36. def compress_image_to_jpeg_range(
  37. image_source,
  38. min_bytes=TARGET_MIN_BYTES,
  39. max_bytes=TARGET_MAX_BYTES, ):
  40. """
  41. 将图片压成 JPEG,尽量使体积落在 [min_bytes, max_bytes]。
  42. image_source: 本地路径(str) 或 原始字节(bytes)。
  43. """
  44. if isinstance(image_source, bytes):
  45. img = Image.open(BytesIO(image_source))
  46. else:
  47. img = Image.open(image_source)
  48. base = _prepare_rgb(img)
  49. scale = 1.0
  50. while scale >= 0.05:
  51. if scale < 1.0:
  52. w, h = base.size
  53. cur = base.resize(
  54. (max(1, int(w * scale)), max(1, int(h * scale))),
  55. _RESAMPLE,
  56. )
  57. else:
  58. cur = base
  59. lo, hi = 1, 95
  60. while lo <= hi:
  61. mid = (lo + hi) // 2
  62. data = _jpeg_bytes(cur, mid)
  63. n = len(data)
  64. if min_bytes <= n <= max_bytes:
  65. return data
  66. if n > max_bytes:
  67. hi = mid - 1
  68. else:
  69. lo = mid + 1
  70. n_min_q = len(_jpeg_bytes(cur, 1))
  71. if n_min_q > max_bytes:
  72. scale *= 0.82
  73. continue
  74. n_max_q = len(_jpeg_bytes(cur, 95))
  75. if n_max_q < min_bytes:
  76. return _jpeg_bytes(cur, 95)
  77. best = None
  78. best_dist = None
  79. target = (min_bytes + max_bytes) // 2
  80. for q in range(1, 96):
  81. data = _jpeg_bytes(cur, q)
  82. n = len(data)
  83. if min_bytes <= n <= max_bytes:
  84. return data
  85. dist = abs(n - target)
  86. if best_dist is None or dist < best_dist:
  87. best_dist = dist
  88. best = data
  89. return best
  90. return _jpeg_bytes(base, 85)
  91. class AliyunOSSUploader:
  92. """阿里云OSS上传工具类"""
  93. def __init__(self):
  94. self.auth = oss2.Auth(OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_SECRET)
  95. self.bucket = oss2.Bucket(self.auth, f"https://{OSS_ENDPOINT}", OSS_BUCKET_NAME)
  96. self.bucket_name = OSS_BUCKET_NAME
  97. self.endpoint = OSS_ENDPOINT
  98. def upload_image(self, local_file_path, content_type='image/jpeg'):
  99. filename = Path(local_file_path).name
  100. object_name = f"screenshots/{str(time.strftime('%Y%m%d_%H%M%S'))}_{filename}"
  101. if not object_name.lower().endswith(('.jpg', '.jpeg')):
  102. object_name = object_name.rsplit('.', 1)[0] + '.jpg'
  103. compressed = compress_image_to_jpeg_range(local_file_path)
  104. headers = {'Content-Type': 'image/jpeg'}
  105. print(object_name)
  106. self.bucket.put_object(object_name, compressed, headers=headers)
  107. return f"https://{self.bucket_name}.{self.endpoint}/{object_name}"
  108. def upload_from_bytes(self, image_data, filename, content_type='image/jpeg'):
  109. filename = f"screenshots/{time.strftime('%Y%m%d_%H%M%S')}_{filename}.jpg"
  110. compressed = compress_image_to_jpeg_range(image_data)
  111. headers = {'Content-Type': 'image/jpeg'}
  112. self.bucket.put_object(filename, compressed, headers=headers)
  113. return f"https://{self.bucket_name}.{self.endpoint}/{filename}"
  114. # 使用示例
  115. if __name__ == "__main__":
  116. uploader = AliyunOSSUploader()
  117. # url = uploader.upload_image("screenshots/68906391.jpg", content_type='image/jpeg')
  118. # print(f"上传成功:{url}")
  119. image_data = open("screenshots/68906391.jpg", "rb")
  120. url = uploader.upload_image(image_data, "68906391",content_type='image/jpeg')