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

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

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

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

505閲覧

WEBアプリ開発でPDFファイルアップロード時に404エラーが発生する問題について

mok__

総合スコア0

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2024/10/24 00:35

実現したいこと

HTMLで記述した画面のアップロードボタン押下後にサーバの指定のディレクトリにPDFをアップロードする処理を行いたい。
・エンドポイント : /api/upload
・アップロード先のディレクトリ : /var/uploads

※確認済みの事項
・エンドポイントのパスは正しく設定されている
・ファイルのバリデーションは正常に動作
・フォームデータは正しく構築されている

発生している問題・分からないこと

アップロードボタン押下時に404エラーが発生

エラーメッセージ

error

1Failed to load resource: the server responded with a status of 404 (Not Found) 2Response status: 404 3Error response: Not Found 4Response is not JSON: SyntaxError: Unexpected token 'N', "Not Found" is not valid JSON

該当のソースコード

Javascript

1document.addEventListener('DOMContentLoaded', function() { 2 // 要素の取得 3 const fileInput = document.getElementById('file-input'); 4 const previewContainer = document.getElementById('preview-container'); 5 const uploadButton = document.getElementById('upload-btn'); 6 const cancelButton = document.getElementById('cancel-btn'); 7 const radioButtons = document.querySelectorAll('input[name="file-type"]'); 8 9 let selectedFile = null; 10 11 // ファイル選択時の処理 12 fileInput.addEventListener('change', () => { 13 const file = fileInput.files[0]; 14 if (!file) { 15 clearUploadForm(); 16 return; 17 } 18 19 // 基本バリデーション 20 if (file.size > 5 * 1024 * 1024) { 21 alert('ファイルサイズが5MBを超えています'); 22 clearUploadForm(); 23 return; 24 } 25 26 if (file.type !== 'application/pdf') { 27 alert('PDFファイルのみアップロード可能です'); 28 clearUploadForm(); 29 return; 30 } 31 32 // プレビュー表示 33 const fileURL = URL.createObjectURL(file); 34 previewContainer.innerHTML = `<object data="${fileURL}" type="application/pdf" width="100%" height="600px"></object>`; 35 selectedFile = file; 36 }); 37 38 // アップロード処理 39 uploadButton.addEventListener('click', async (e) => { 40 try { 41 e.preventDefault(); 42 43 if (!selectedFile) { 44 alert('ファイルが選択されていません'); 45 return; 46 } 47 48 let selectedType = ''; 49 for (const radio of radioButtons) { 50 if (radio.checked) { 51 selectedType = radio.value; 52 break; 53 } 54 } 55 56 if (!selectedType) { 57 alert('ファイルタイプを選択してください'); 58 return; 59 } 60 61 const formData = new FormData(); 62 formData.append('file', selectedFile); 63 formData.append('file_type', selectedType); 64 65 const response = await fetch('/api/upload', { 66 method: 'POST', 67 body: formData 68 }); 69 70 if (!response.ok) { 71 throw new Error(`HTTP error! status: ${response.status}`); 72 } 73 74 const data = await response.json(); 75 alert(data.message || 'アップロード完了'); 76 clearUploadForm(); 77 } catch (error) { 78 alert(`アップロードエラー: ${error.message}`); 79 } 80 }); 81 82 // フォームクリア 83 function clearUploadForm() { 84 fileInput.value = ''; 85 previewContainer.innerHTML = ''; 86 selectedFile = null; 87 radioButtons.forEach(radio => radio.checked = false); 88 } 89 90 // キャンセル処理 91 cancelButton.addEventListener('click', (e) => { 92 e.preventDefault(); 93 clearUploadForm(); 94 }); 95});

Python

1import os 2import json 3import cgi 4import logging 5import datetime 6import configparser 7from sqlalchemy import create_engine, text, MetaData, Table 8from sqlalchemy.exc import SQLAlchemyError 9 10# 基本設定 11logging.basicConfig(filename='/tmp/app.log', level=logging.DEBUG) 12 13def load_config(): 14 try: 15 config = configparser.ConfigParser() 16 config.read('config.env', encoding="utf-8_sig") 17 return config 18 except Exception as e: 19 logging.error(f"Config error: {str(e)}") 20 raise 21 22# アップロード設定 23UPLOAD_FOLDER = '/var/uploads' 24ALLOWED_EXTENSIONS = {'pdf'} 25 26def allowed_file(filename): 27 return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 28 29def validate_file_type(file_type): 30 return file_type in ['type1', 'type2', 'type3'] 31 32def save_file(file_item, file_type): 33 try: 34 if not file_item.filename: 35 raise ValueError('ファイルが選択されていません') 36 37 filename = os.path.basename(file_item.filename) 38 39 # 基本バリデーション 40 if not all([ 41 validate_file_type(file_type), 42 allowed_file(filename), 43 len(file_item.file.read()) <= 5 * 1024 * 1024 44 ]): 45 raise ValueError('ファイルが要件を満たしていません') 46 47 # ファイル保存 48 file_path = os.path.join(UPLOAD_FOLDER, filename) 49 with open(file_path, 'wb') as f: 50 f.write(file_item.file.read()) 51 52 return filename 53 except Exception as e: 54 logging.error(f"Save error: {str(e)}") 55 raise 56 57def application(environ, start_response): 58 try: 59 headers = [ 60 ('Content-Type', 'application/json'), 61 ('Access-Control-Allow-Origin', '*'), 62 ('Access-Control-Allow-Methods', 'POST, OPTIONS'), 63 ('Access-Control-Allow-Headers', 'Content-Type') 64 ] 65 66 # パスチェック 67 if environ.get('PATH_INFO', '').rstrip('/') != '/api/upload': 68 start_response('404 Not Found', headers) 69 return [json.dumps({'error': 'Not Found'}).encode('utf-8')] 70 71 # OPTIONSリクエスト処理 72 if environ['REQUEST_METHOD'] == 'OPTIONS': 73 start_response('200 OK', headers) 74 return [b''] 75 76 # POSTリクエスト処理 77 elif environ['REQUEST_METHOD'] == 'POST': 78 try: 79 form = cgi.FieldStorage( 80 fp=environ['wsgi.input'], 81 environ=environ, 82 keep_blank_values=True 83 ) 84 85 if 'file' not in form: 86 raise ValueError('ファイルがありません') 87 88 saved_filename = save_file( 89 form['file'], 90 form.getvalue('file_type') 91 ) 92 93 start_response('200 OK', headers) 94 return [json.dumps({ 95 'status': 'success', 96 'message': f'アップロード成功: {saved_filename}' 97 }).encode('utf-8')] 98 99 except ValueError as e: 100 start_response('400 Bad Request', headers) 101 return [json.dumps({'error': str(e)}).encode('utf-8')] 102 103 # その他のメソッド 104 else: 105 start_response('405 Method Not Allowed', headers) 106 return [json.dumps({'error': 'Method Not Allowed'}).encode('utf-8')] 107 108 except Exception as e: 109 start_response('500 Internal Server Error', headers) 110 return [json.dumps({'error': 'Internal Server Error'}).encode('utf-8')]

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

ログを細かく出力し、原因を探りましたが解決しませんでした。

補足

リクエストからレスポンスの流れは次のようになってます。
① html ⇒ ② nginx ⇒ ③ unit ⇒ ④ python ⇒ ③ unit ⇒ ② nginx ⇒ ① html

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

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

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

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

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

int32_t

2024/10/25 00:43

Pythonコード実行時に environ.get('PATH_INFO', '').rstrip('/') が何になっているかを確認すべきかと思います。
Lhankor_Mhy

2024/10/25 01:17

『エンドポイントのパスは正しく設定されている』ということは、ご提示のコードでは404が返ってくるが、curlなどのその他の手段でリクエストした場合は404にならない、ということでいいでしょうか?
juner

2024/10/25 01:26

> Response is not JSON: SyntaxError: Unexpected token 'N', "Not Found" is not valid JSON ということは "Not Found" という固定文字列を返しているが json でないのでエラーとなっていると読めるのですが、 戻りの content-type を固定 で json にしていたりするのでしょうか?
guest

回答1

0

404エラーなのですから単純にアップロードの指定先が間違っているだけでは?

たとえばこんなソースで

html

1<script> 2window.addEventListener('DOMContentLoaded', ()=>{ 3 btn.addEventListener('click',()=>{ 4 const file=myfile.files[0]; 5 if(file){ 6 const fr = new FileReader(); 7 fr.onload = e=>{ 8 const body=new FormData(); 9 body.append("userfile", new Blob([e.target.result],{"type":file.type}),file.name ); 10 const url='/api/upload'; 11 fetch(url,{ 12 method:"post", 13 body, 14 }).then(res=>{ 15 if(res.ok) return res.text(); 16 throw new Error(res.status); 17 }).then(txt=>{ 18 console.log(txt); 19 }).catch(err=>{ 20 console.error(err); 21 }); 22 } 23 fr.readAsArrayBuffer(file); 24 } 25 }); 26}); 27</script> 28<input type="file" id="myfile" name="myfile" accept=".pdf"> 29<input type="button" id="btn" value="send">

受け取り側が「/api/upload」というディレクトリ(?)になっていますが、受取用のプログラム(API?)を指定する必要があるように見えますが、なにをもって「アップロード先は合っていました」と判断されたのでしょうか?

ファイルの転送ができているのにAPI側が何らかの理由(送られてきたファイルが想定とちがうとかなんらかの必要なトークンがぬけてるとか)でわざと404エラーを吐いている可能性はあるので、その場合はAPI側の仕様を確認してもらう必要があります

投稿2024/10/24 03:20

編集2024/10/25 01:08
yambejp

総合スコア116810

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

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

mok__

2024/10/25 00:14

確認しましたが、アップロード先は合っていました。
yambejp

2024/10/25 01:07

一応fetchをfilereader形式にしてみましたのでテストしてみてください
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問