前提・実現したいこと
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>
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/06/18 01:35 編集