質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Google App Engine

Google App Engineは、Googleの管理するデータセンター上でウェブアプリケーションの開発が可能なクラウドコンピュータ技術です。Java、Python、Go用にSDKが用意されています。

Q&A

解決済

2回答

2786閲覧

DjangoでGoogle Cloud Storageのアップロードする署名付きURLを作ってもError413になる

TRUM

総合スコア0

Google Cloud Storage

Google Cloud Storageは、グーグル社が提供しているクラウドベースのデベロッパー・企業向けストレージサービス。可用性に優れ、APIで操作可能なため、データのアーカイブ保存やアプリケーションのコンテンツ提供など様々な用途に活用できます。

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Google App Engine

Google App Engineは、Googleの管理するデータセンター上でウェブアプリケーションの開発が可能なクラウドコンピュータ技術です。Java、Python、Go用にSDKが用意されています。

0グッド

0クリップ

投稿2021/06/13 10:21

編集2021/06/18 04:20

前提・実現したいこと

djangoを使って動画投稿サイトの作成をし、「GoogleAppEngin」にデプロイを行いました。動画の視聴や画像の更新などの通信は可能なのですがアカウントから動画をアップロードすると以下のエラーメッセージが発生します。

エラーメッセージ

”””
error 413 That’s an error.

Your client issued a request that was too large. That’s all we know.
”””

エラーの原因

原因としてGoogle App Enginが関わっており、一回のリクエストの最大サイズが32MBまでとなっています。解決策として「GoogleCloudStorage」や「S3」の署名付きURLを使って直接アップロードするとのことです。いろいろ試してみたのですが動画ファイルをアップロードする署名付きURLの作成&使用することができず、エラー413が出てしまい白旗です。どうしたらエラー413を出さずに32MB以上の動画のアップロードができるようになるか教えていただければ幸いです。

試したこと

こちらのサイト参考にapp/views.pyに組み込みました。

views

1#省略 2from google.cloud import storage 3#省略 4class MovieUpload(OnlyYouMixin,generic.CreateView,): 5 template_name = 'app/movie_upload.html' 6 form_class = Upload 7 8 def form_valid(self,form,): 9 movie = form.save(commit=False) 10 movie.user = self.request.user 11 CLIENT = storage.Client.from_service_account_json('GAE defaultservice accountkey.json') 12 bucket = CLIENT.bucket(settings.GS_BUCKET_NAME) 13 14 blob = bucket.blob(str(movie.video)) 15 # Blob data gets input form data and change it str data. 16 #It is name of file to be saved/uploaded to storage 17 url = blob.generate_signed_url( 18 version='v4', 19 expiration=datetime.timedelta(minutes=60), 20 method='POST', 21 content_type="application/octet-stream", 22       ) 23 movie.save() 24 25 26 def get_success_url(self): 27 return resolve_url('movie:user_detail', pk=self.kwargs['pk'])

###補足
django-storageも導入しており、画像や動画を一時的に見ることができるURLがあるのでダウンロードに関する署名付きURLはできていると考えます。またこの方法でもAPP Engineではアップロードはできませんが、LocalHOST上ではうまく動くので余計混乱しています。

###追記
attakei様のアドバイスを受けてjavascript(ajax)を利用したアップロードフォームをこちらのサイトを参考に組み込んでみました。LocalhostでテストしてみるとPOST後の処理が正常に行われているのかが怪しいものとなります。どこが間違っているのかをご指摘いただければ幸いです。

views

1class MovieUpload(OnlyYouMixin,generic.CreateView,): 2 template_name = 'app/movie_upload.html' 3 form_class = Upload 4 5 def form_valid(self,form,): 6 movie = form.save(commit=False) 7 movie.user = self.request.user 8 movie.save() 9 10 11 def get_success_url(self): 12 return resolve_url('movie:user_detail', pk=self.kwargs['pk']) 13 14 15 16def get_signed_url(request,pk): 17 if request.method == 'POST' and request.is_ajax(): 18 filename = request.POST.get('filename') 19 filetype = request.POST.get('type') 20 filesize = request.POST.get('size', 0) 21 22 # build the URL path using whatever information 23 fullpath = '/path/'+filename 24 25 # create the blob - the blob has to be created in order to get a signed URL 26 blob = default_storage.open(fullpath, 'wb') 27 28 # generate the signed URL through the blob object - don't use django-storages (yet) 29 signed_url = blob.blob.generate_signed_url(expiration=default_storage.expiration, method='PUT', content_type=filetype) 30 31 # This is what you'd do with djagno-storages 32 #signed_url = default_storage.url(fullpath) 33 34 # Send the signed URL back. I also send the path back because I want to display the uploaded image (relative path) 35 return JsonResponse({ 'signed_url': signed_url, 'url': settings.MEDIA_URL + fullpath }) 36 37 # Probably a terrible way to respond. You do you. 38 return JsonResponse({})

url

1path('user_datail/User<int:pk>/upload',views.MovieUpload.as_view(),name='movie_upload_in'), 2path('user_datail/User<int:pk>/upload/sign',views.get_signed_url,name='sign'),

html

1<form method="POST" enctype="multipart/form-data"action="{% url 'movie:sign' user.pk %}" > 2<div class="container mt-3"> 3 <button id ="Upload"class="btn btn-info btn-lg" name="next">Upload</button> 4 <div class="field"> 5 {{ form.title.label }} 6 {{ form.title }} 7 <span class="helptext">{{ form.title.help_text }}</span> 8 {{ form.title.errors }} 9 </div> 10 <div class="field"> 11 {{ form.name.label }} 12 {{ form.name }} 13 </div> 14 <div class="field"> 15 {{ form.publick.label }} 16 {{ form.publick }} 17 <label for="id_publick">Release</label> <span class="helptext"></span> 18 {{ form.publick.errors }} 19 </div> 20 <div class="field" id="cover_image"> 21 {{ form.video.label_tag }} 22 {{ form.video }} 23 <span class="helptext">{{ form.video.help_text }}</span> 24</div> 25 26<script> 27 $('#id_video').fileupload({ 28 // most of these doesn't matter and will be removed 29 // because the add() function does the actual upload now 30 dropZone: $('#id_video'), 31 add: function(e, data) { 32 $.each(data.files, function(index, file) { 33 // pack our data to get signature url 34 var formData = new FormData(); 35 formData.append('filename', file.name); 36 formData.append('type', file.type); 37 formData.append('size', file.size); 38 39 // Step 3: get our signature URL 40 $.ajax({ 41 url: "{% url 'movie:sign' user.pk %}", 42 type: 'POST', 43 processData: false, 44 contentType: false, 45 dataType: 'json', 46 headers: { 47 'X-CSRFToken': Cookies.get('csrftoken'), 48 }, 49 data: formData 50 }).done(function (data) { 51 // Step 5: got our url, push to GCS 52 const xhr = new XMLHttpRequest(); 53 if ('withCredentials' in xhr) { 54 xhr.open("PUT", data.signed_url, true); 55 } 56 else if (typeof XDomainRequest !== 'undefined') { 57 xhr = new XDomainRequest(); 58 xhr.open("PUT", data.signed_url); 59 } 60 else { 61 xhr = null; 62 } 63 64 xhr.onload = () => { 65 const status = xhr.status; 66 if (status === 200) { 67 //alert("File is uploaded"); 68 // show the image (uploadDone is separate function) 69 uploadDone(data.url) 70 } else { 71 alert("Failed to upload"); 72 } 73 }; 74 75 xhr.onerror = () => { 76 alert("Failed to upload"); 77 }; 78 79 xhr.setRequestHeader('Content-Type', file.type); 80 xhr.send(file); 81 }); 82 }); 83 }, 84 done: function (e, data) { 85 } 86}); 87 </script>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

概要

記載されているコードは「動画を直接Cloud Storageにアップロードしている」実装になっていません。
あくまで、AppEngine上へのリクエストとして動画アップロードを行い、アップロードされた動画の署名付きURL生成しているだけです。
つまり、この実装ではレスポンス上の制限は回避できても、リクエストのサイズ制限を回避できません。

(ローカルホストでの挙動に問題がないのは、アップロード時にサイズ制限がないためです)

簡易的な図

おそらく、大本の実装がこうです。

[ブラウザ] --(アップロード)-|-> [AppEngine] --> [CloudStorage] [ブラウザ] <-(ダウンロード)-|-- [AppEngine] <-- [CloudStorage] ↑ここに、上下ともに32MBのサイズ制限

質問内の実装は、上記をこう変えたものと捉えられます

[ブラウザ] --(アップロード)-|-> [AppEngine] --> [CloudStorage] [ブラウザ] <----------------------------------- [CloudStorage]
  • 署名付きURLでCloudStorageに直接アクセスできるので下りは回避できる
  • アップロード自体はAppEngineのままのため、32MBを超えた時点でTooLargeは発生する

どうするか

考え方としては以下の2通りになると思います。
理論上は出来るはずですが、実際に手を動かしてみたことがない範囲のはないsなので、コード等の提供は出来ません。

  • App Engineをやめて、リクエストサイズ制限のない環境にデプロイする
  • JavaScriptと連携して、安全な設計でブラウザから直接Cloud Storageにアップロードするように実装

投稿2021/06/13 10:55

attakei

総合スコア2740

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TRUM

2021/06/18 01:35 編集

Javascriptの項目で安全な設計と書いてありますが、どういうことをすれば安全な設計になるのでしょうか?JavaScriptファイルも含めてwebインスペクター(MAC)を覗いてしまえばコードがわかってしまうので、本人にしかわからないレベルでの見難いコードを書くことぐらいしか思いつきません。
guest

0

自己解決

Javascript と 'rest_framework'を使いAWSからアップロードできました。記載するにかなり複雑なのでこちらに記載しています。

投稿2021/09/17 23:06

TRUM

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問