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

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

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

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

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

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

1545閲覧

Laravelでmultipart/form-data(画像)のリクエストがnull

whitehackerh

総合スコア2

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

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

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2023/01/06 21:46

編集2023/01/07 03:14

前提

Reactでaxiosを使ってLaravel側に画像とそれに関連するデータをリクエストしようとしています。

https://codesandbox.io/s/react-uploady-crop-and-upload-with-react-easy-crop-5g7vw?file=/src/App.js
↑を参考にトリミングした画像をサーバー側に送ろうとしています。

実現したいこと

  • ▲▲リクエストパラメータを受け取れるようにする

発生している問題・エラーメッセージ

controllerで$request->input や $request->file をしてみてもnullになる
イメージ説明

該当のソースコード

リクエストパラメータ
イメージ説明

components/modules/crop/CropImage.js

javascript

1import React, { useState, useCallback } from "react"; 2import styled from "styled-components"; 3import Cropper from "react-easy-crop"; 4import getCroppedImg from "./CropUtils"; 5import "./Crop.css"; 6import { withTokenRequest, formData } from '../../../http'; 7import { PREVIEW_TYPES } from "@rpldy/upload-preview"; 8import { 9 withRequestPreSendUpdate, 10 useItemFinalizeListener, 11 useItemProgressListener 12} from "@rpldy/uploady"; 13 14const PreviewButtons = ({ 15 finished, 16 crop, 17 updateRequest, 18 onUploadCancel, 19 onUploadCrop 20 }) => { 21 return ( 22 <ButtonsWrapper> 23 <button 24 style={{ 25 display: !finished && updateRequest && crop ? "block" : "none" 26 }} 27 onClick={onUploadCrop} 28 > 29 Upload Cropped 30 </button> 31 <button 32 style={{ 33 display: !finished && updateRequest && crop ? "block" : "none" 34 }} 35 onClick={onUploadCancel} 36 > 37 Cancel 38 </button> 39 </ButtonsWrapper> 40 ); 41 }; 42 43/* API REQUEST */ 44 function setProfileIcon(item, formDataParam) { 45 const submitData = new FormData(); 46 submitData.append("requestParameters", JSON.stringify({user_id: localStorage.getItem('user_id')})); 47 submitData.append("image", item); 48 withTokenRequest.post('/setProfileIcon', submitData, 49 { 50 headers: formDataParam // http.jsの定数 51 }) 52 .then((result) => { 53 console.log(result); 54 }); 55 } 56 57export const ItemPreviewWithCrop = withRequestPreSendUpdate((props) => { 58 const { 59 id, 60 url, 61 isFallback, 62 type, 63 updateRequest, 64 requestData, 65 previewMethods, 66 aspectProps, 67 api, 68 aspectControllButtonsVisible, 69 inputTextVisible 70 } = props; 71 72 formData.Authorization = `${localStorage.getItem('token_type')} ${localStorage.getItem('access_token')}`; 73 const [uploadState, setUploadState] = useState(UPLOAD_STATES.NONE); 74 const [croppedImg, setCroppedImg] = useState(null); 75 const [values, setValues] = useState(null); 76 const [aspect, setAspect] = useState(aspectProps); 77 78 //data for react-easy-crop 79 const [crop, setCrop] = useState({ x: 0, y: 0 }); 80 const [zoom, setZoom] = useState(1); 81 const [croppedAreaPixels, setCroppedAreaPixels] = useState(null); 82 83 const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => { 84 // console.log(croppedArea, croppedAreaPixels); 85 setCroppedAreaPixels(croppedAreaPixels); 86 }, []); 87 88 function handleChange(e) { 89 const target = e.target; 90 const value = target.value; 91 const name = target.name; 92 setValues({ ...values, [name]: value }); 93 } 94 95 const isFinished = uploadState === UPLOAD_STATES.FINISHED; 96 97 useItemProgressListener(() => setUploadState(UPLOAD_STATES.UPLOADING), id); 98 useItemFinalizeListener(() => setUploadState(UPLOAD_STATES.FINISHED), id); 99 100 // Upload Cropped ボタン押下時イベント 101 const onUploadCrop = useCallback(async () => { 102 if (updateRequest && croppedAreaPixels) { 103 const [croppedBlob, croppedUri] = await getCroppedImg( 104 url, 105 croppedAreaPixels 106 ); 107 108 requestData.items[0].file = croppedBlob; 109 110 updateRequest({ items: requestData.items }); 111 switch (api) { 112 case 'setProfileIcon': 113 setProfileIcon(requestData.items[0].file, formData); 114 break; 115 default: 116 break; 117 } 118 } 119 }, [url, requestData, updateRequest, croppedAreaPixels]); 120 121 const onUploadCancel = useCallback(() => { 122 updateRequest(false); 123 if (previewMethods.current?.clear) { 124 previewMethods.current.clear(); 125 } 126 }, [updateRequest, previewMethods]); 127 128 return isFallback || type !== PREVIEW_TYPES.IMAGE ? ( 129 null 130 ) : ( 131 <> 132 {requestData && uploadState === UPLOAD_STATES.NONE ? ( 133 <div className="crop-view"> 134 <div className="crop-container"> 135 <Cropper 136 image={url} 137 crop={crop} 138 zoom={zoom} 139 aspect={aspect} 140 onCropChange={setCrop} 141 onCropComplete={onCropComplete} 142 onZoomChange={setZoom} 143 /> 144 </div> 145 <div className="aspectControll" style={{ position: 'absolute', display: aspectControllButtonsVisible ? '' : 'none' }}> 146 <button onClick={() => setAspect(1/1)}>Square</button> 147 <button onClick={() => setAspect(5/4)}>Portrait</button> 148 <button onClick={() => setAspect(1/1.91)}>Landscape</button> 149 </div> 150 <div className="inputText" style={{ position: 'absolute', display: inputTextVisible ? '' : 'none' }}> 151 <textarea onChange={handleChange}></textarea> 152 </div> 153 <div className="controls"> 154 <input 155 type="range" 156 value={zoom} 157 min={1} 158 max={3} 159 step={0.1} 160 aria-labelledby="Zoom" 161 onChange={(e) => { 162 setZoom(e.target.value); 163 }} 164 className="zoom-range" 165 /> 166 </div> 167 </div> 168 ) : ( 169 null 170 )} 171 <PreviewButtons 172 finished={isFinished} 173 crop={crop} 174 updateRequest={updateRequest} 175 onUploadCancel={onUploadCancel} 176 onUploadCrop={onUploadCrop} 177 /> 178 </> 179 ); 180 });

http.js

javascript

1import axios from 'axios'; 2const apiUrl = "http://127.0.0.1:80/api"; 3export const formData = { 4 "Content-type": "multipart/form-data", 5 "Authorization": '', 6} 7export const withTokenRequest = axios.create({ 8 baseURL:apiUrl, 9})

comoponents/pages/accountsettings/ImageSettings.js

javascript

1import React, { useRef } from "react"; 2import Uploady from "@rpldy/uploady"; 3import UploadButton from "@rpldy/upload-button"; 4import UploadPreview from "@rpldy/upload-preview"; 5import "../../modules/crop/Crop.css"; 6import "../../modules/crop/CropImage"; 7import noImage from "../../../assets/img/no image/noimage.png" 8import { ItemPreviewWithCrop, mockSenderEnhancer } from "../../modules/crop/CropImage"; 9 10const ImageSettings = () => { 11return ( 12 <> 13 <div style={mainContents}> 14 <Uploady 15 multiple={false} 16 destination={{ url: "[upload-url]" }} 17 > 18 <div className="IconSettings" style={iconSettings}> 19 Icon<br></br> 20 <img src={noImage} alt="picture" style={iconStyle}></img><br></br> 21 <UploadButton>SETTINGS</UploadButton> 22 <br /> 23 <UploadPreview 24 PreviewComponent={ItemPreviewWithCrop} // CropImage.js の ItemPreviewWithCrop を呼び出します 25 previewComponentProps={{ 26 previewMethods: previewMethodsRef, 27 aspect: 1 / 1, 28 api: 'setProfileIcon', 29 aspectControllButtonsVisible: false, 30 inputTextVisible: false }} 31 previewMethodsRef={previewMethodsRef} 32 /> 33 </div> 34 </Uploady> 35 </div> 36 </> 37 ); 38};

ディレクトリ構成 (見づらくてすみません)
components
┠ modules
┃ ┗crop
┃ ┠Crop.css
┃ ┠CropImage.js (実際にリクエストするところ)
┃ ┗CropUtils.js (画像生成処理など)
┠ pages
┃ ┗accountSettings
┃ ┗ImageSettings.js (モーダルの呼び出し元)

試したこと

php

1$a = $request->input('image'); 2$b = $request->image; 3$c = $request->file('image'); 4$d = $request->user_id; 5$e = $request->input('user_id'); 6$f = $request->requestParameters; 7$g = $request->all();

$request->all() 以外すべてnullになっています。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

KoichiSugiyama

2023/01/07 00:13

開示されている情報量が少なく、第三者からは推測でしか考えにくいですので、問題の切り分けのためにご自身の環境で下記を確認してみてください。 ・phpの設定(php.iniや.htaccess)でファイル送信が可能な設定になっているか(ファイルサイズなども含めて) ・Laravel側でmultipart/form-dataを受信できるようになっているか ・簡単なテストフォームをhtmlやphpで作ってみて、javascriptなしで送信できるかどうか あと、ここで質問する前に調査した内容(検索ワード「○○」で検索した等)があればそれも開示していただいた方が、回答する方が余計な手間をかけずに済みます。
m.ts10806

2023/01/07 01:33

view側のコードもご提示を。 setProfileIconを呼び出してるところも。
whitehackerh

2023/01/07 02:37 編集

ImageSettings.js のUploadボタンを押下することで CropImage.jsのItemPreviewWithCrop が呼び出されます。 ItemPreviewWithCrop の Upload Croppedボタンを押下することで APIリクエスト処理が実行されます (setProfileIconメソッド) 追記したことで逆にわかりにくくなってしまったらすみません。
guest

回答1

0

自己解決

http.js

javascript

1"Content-type": "multipart/form-data",

Content-typet を大文字にしたら値が取れるようになりました。

投稿2023/01/09 11:54

whitehackerh

総合スコア2

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

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

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

質問する

関連した質問