質問編集履歴

5

title fix

2023/01/07 03:14

投稿

whitehackerh
whitehackerh

スコア2

test CHANGED
@@ -1 +1 @@
1
- Laravelでmultipart/form-data(画像)のリクエストが受け取れない
1
+ Laravelでmultipart/form-data(画像)のリクエストがnull
test CHANGED
File without changes

4

不要な文言削除

2023/01/07 02:38

投稿

whitehackerh
whitehackerh

スコア2

test CHANGED
File without changes
test CHANGED
@@ -7,8 +7,7 @@
7
7
 
8
8
  ### 実現したいこと
9
9
 
10
- ここに実現したいことを箇条書きで書いてください。
11
- - [ ] ▲▲リクエストを受け取れるようにする
10
+ - [ ] ▲▲リクエストパラメータを受け取れるようにする
12
11
 
13
12
  ### 発生している問題・エラーメッセージ
14
13
 

3

ImageSettings.js の上方修正

2023/01/07 02:34

投稿

whitehackerh
whitehackerh

スコア2

test CHANGED
File without changes
test CHANGED
@@ -268,7 +268,7 @@
268
268
  ┃ ┗CropUtils.js (画像生成処理など)
269
269
  ┠ pages
270
270
  ┃ ┗accountSettings
271
- ┃ ┗ImageSettings.js (setProfileIconの呼び出し元)
271
+ ┃ ┗ImageSettings.js (モーダルの呼び出し元)
272
272
 
273
273
 
274
274
  ### 試したこと

2

view側のソース追記、setProfileIconを呼び出してる箇所の追記

2023/01/07 02:33

投稿

whitehackerh
whitehackerh

スコア2

test CHANGED
File without changes
test CHANGED
@@ -1,6 +1,9 @@
1
1
  ### 前提
2
2
 
3
3
  Reactでaxiosを使ってLaravel側に画像とそれに関連するデータをリクエストしようとしています。
4
+
5
+ https://codesandbox.io/s/react-uploady-crop-and-upload-with-react-easy-crop-5g7vw?file=/src/App.js
6
+ ↑を参考にトリミングした画像をサーバー側に送ろうとしています。
4
7
 
5
8
  ### 実現したいこと
6
9
 
@@ -17,23 +20,256 @@
17
20
  リクエストパラメータ
18
21
  ![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2023-01-07/455c48a5-7282-4466-bcef-5b0f537c2a78.png)
19
22
 
23
+ components/modules/crop/CropImage.js
20
24
  ```javascript
25
+ import React, { useState, useCallback } from "react";
26
+ import styled from "styled-components";
27
+ import Cropper from "react-easy-crop";
28
+ import getCroppedImg from "./CropUtils";
29
+ import "./Crop.css";
30
+ import { withTokenRequest, formData } from '../../../http';
31
+ import { PREVIEW_TYPES } from "@rpldy/upload-preview";
32
+ import {
33
+ withRequestPreSendUpdate,
34
+ useItemFinalizeListener,
35
+ useItemProgressListener
36
+ } from "@rpldy/uploady";
37
+
38
+ const PreviewButtons = ({
39
+ finished,
40
+ crop,
41
+ updateRequest,
42
+ onUploadCancel,
43
+ onUploadCrop
44
+ }) => {
45
+ return (
46
+ <ButtonsWrapper>
47
+ <button
48
+ style={{
49
+ display: !finished && updateRequest && crop ? "block" : "none"
50
+ }}
51
+ onClick={onUploadCrop}
52
+ >
53
+ Upload Cropped
54
+ </button>
55
+ <button
56
+ style={{
57
+ display: !finished && updateRequest && crop ? "block" : "none"
58
+ }}
59
+ onClick={onUploadCancel}
60
+ >
61
+ Cancel
62
+ </button>
63
+ </ButtonsWrapper>
64
+ );
65
+ };
66
+
67
+ /* API REQUEST */
21
- function setProfileIcon(item) {
68
+ function setProfileIcon(item, formDataParam) {
22
69
  const submitData = new FormData();
23
- submitData.append("requestParameters", JSON.stringify({user_id: 1}));
70
+ submitData.append("requestParameters", JSON.stringify({user_id: localStorage.getItem('user_id')}));
24
71
  submitData.append("image", item);
25
- axios.post('/setProfileIcon', submitData,
72
+ withTokenRequest.post('/setProfileIcon', submitData,
26
73
  {
27
- headers: {
28
- "Content-type": "multipart/form-data",
74
+ headers: formDataParam // http.jsの定数
29
- "Authorization": `トークン情報`,
30
- }
31
75
  })
32
76
  .then((result) => {
33
77
  console.log(result);
34
78
  });
79
+ }
80
+
81
+ export const ItemPreviewWithCrop = withRequestPreSendUpdate((props) => {
82
+ const {
83
+ id,
84
+ url,
85
+ isFallback,
86
+ type,
87
+ updateRequest,
88
+ requestData,
89
+ previewMethods,
90
+ aspectProps,
91
+ api,
92
+ aspectControllButtonsVisible,
93
+ inputTextVisible
94
+ } = props;
95
+
96
+ formData.Authorization = `${localStorage.getItem('token_type')} ${localStorage.getItem('access_token')}`;
97
+ const [uploadState, setUploadState] = useState(UPLOAD_STATES.NONE);
98
+ const [croppedImg, setCroppedImg] = useState(null);
99
+ const [values, setValues] = useState(null);
100
+ const [aspect, setAspect] = useState(aspectProps);
101
+
102
+ //data for react-easy-crop
103
+ const [crop, setCrop] = useState({ x: 0, y: 0 });
104
+ const [zoom, setZoom] = useState(1);
105
+ const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
106
+
107
+ const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
108
+ // console.log(croppedArea, croppedAreaPixels);
109
+ setCroppedAreaPixels(croppedAreaPixels);
110
+ }, []);
111
+
112
+ function handleChange(e) {
113
+ const target = e.target;
114
+ const value = target.value;
115
+ const name = target.name;
116
+ setValues({ ...values, [name]: value });
117
+ }
118
+
119
+ const isFinished = uploadState === UPLOAD_STATES.FINISHED;
120
+
121
+ useItemProgressListener(() => setUploadState(UPLOAD_STATES.UPLOADING), id);
122
+ useItemFinalizeListener(() => setUploadState(UPLOAD_STATES.FINISHED), id);
123
+
124
+ // Upload Cropped ボタン押下時イベント
125
+ const onUploadCrop = useCallback(async () => {
126
+ if (updateRequest && croppedAreaPixels) {
127
+ const [croppedBlob, croppedUri] = await getCroppedImg(
128
+ url,
129
+ croppedAreaPixels
130
+ );
131
+
132
+ requestData.items[0].file = croppedBlob;
133
+
134
+ updateRequest({ items: requestData.items });
135
+ switch (api) {
136
+ case 'setProfileIcon':
137
+ setProfileIcon(requestData.items[0].file, formData);
138
+ break;
139
+ default:
140
+ break;
141
+ }
142
+ }
143
+ }, [url, requestData, updateRequest, croppedAreaPixels]);
144
+
145
+ const onUploadCancel = useCallback(() => {
146
+ updateRequest(false);
147
+ if (previewMethods.current?.clear) {
148
+ previewMethods.current.clear();
149
+ }
150
+ }, [updateRequest, previewMethods]);
151
+
152
+ return isFallback || type !== PREVIEW_TYPES.IMAGE ? (
153
+ null
154
+ ) : (
155
+ <>
156
+ {requestData && uploadState === UPLOAD_STATES.NONE ? (
157
+ <div className="crop-view">
158
+ <div className="crop-container">
159
+ <Cropper
160
+ image={url}
161
+ crop={crop}
162
+ zoom={zoom}
163
+ aspect={aspect}
164
+ onCropChange={setCrop}
165
+ onCropComplete={onCropComplete}
166
+ onZoomChange={setZoom}
167
+ />
168
+ </div>
169
+ <div className="aspectControll" style={{ position: 'absolute', display: aspectControllButtonsVisible ? '' : 'none' }}>
170
+ <button onClick={() => setAspect(1/1)}>Square</button>
171
+ <button onClick={() => setAspect(5/4)}>Portrait</button>
172
+ <button onClick={() => setAspect(1/1.91)}>Landscape</button>
173
+ </div>
174
+ <div className="inputText" style={{ position: 'absolute', display: inputTextVisible ? '' : 'none' }}>
175
+ <textarea onChange={handleChange}></textarea>
176
+ </div>
177
+ <div className="controls">
178
+ <input
179
+ type="range"
180
+ value={zoom}
181
+ min={1}
182
+ max={3}
183
+ step={0.1}
184
+ aria-labelledby="Zoom"
185
+ onChange={(e) => {
186
+ setZoom(e.target.value);
187
+ }}
188
+ className="zoom-range"
189
+ />
190
+ </div>
191
+ </div>
192
+ ) : (
193
+ null
194
+ )}
195
+ <PreviewButtons
196
+ finished={isFinished}
197
+ crop={crop}
198
+ updateRequest={updateRequest}
199
+ onUploadCancel={onUploadCancel}
200
+ onUploadCrop={onUploadCrop}
201
+ />
202
+ </>
203
+ );
204
+ });
205
+ ```
206
+
207
+ http.js
208
+ ```javascript
209
+ import axios from 'axios';
210
+ const apiUrl = "http://127.0.0.1:80/api";
211
+ export const formData = {
212
+ "Content-type": "multipart/form-data",
213
+ "Authorization": '',
35
214
  }
215
+ export const withTokenRequest = axios.create({
216
+ baseURL:apiUrl,
217
+ })
36
- ```
218
+ ```
219
+
220
+ comoponents/pages/accountsettings/ImageSettings.js
221
+ ```javascript
222
+ import React, { useRef } from "react";
223
+ import Uploady from "@rpldy/uploady";
224
+ import UploadButton from "@rpldy/upload-button";
225
+ import UploadPreview from "@rpldy/upload-preview";
226
+ import "../../modules/crop/Crop.css";
227
+ import "../../modules/crop/CropImage";
228
+ import noImage from "../../../assets/img/no image/noimage.png"
229
+ import { ItemPreviewWithCrop, mockSenderEnhancer } from "../../modules/crop/CropImage";
230
+
231
+ const ImageSettings = () => {
232
+ return (
233
+ <>
234
+ <div style={mainContents}>
235
+ <Uploady
236
+ multiple={false}
237
+ destination={{ url: "[upload-url]" }}
238
+ >
239
+ <div className="IconSettings" style={iconSettings}>
240
+ Icon<br></br>
241
+ <img src={noImage} alt="picture" style={iconStyle}></img><br></br>
242
+ <UploadButton>SETTINGS</UploadButton>
243
+ <br />
244
+ <UploadPreview
245
+ PreviewComponent={ItemPreviewWithCrop} // CropImage.js の ItemPreviewWithCrop を呼び出します
246
+ previewComponentProps={{
247
+ previewMethods: previewMethodsRef,
248
+ aspect: 1 / 1,
249
+ api: 'setProfileIcon',
250
+ aspectControllButtonsVisible: false,
251
+ inputTextVisible: false }}
252
+ previewMethodsRef={previewMethodsRef}
253
+ />
254
+ </div>
255
+ </Uploady>
256
+ </div>
257
+ </>
258
+ );
259
+ };
260
+ ```
261
+
262
+ ディレクトリ構成 (見づらくてすみません)
263
+ components
264
+ ┠ modules
265
+ ┃ ┗crop
266
+ ┃ ┠Crop.css
267
+ ┃ ┠CropImage.js (実際にリクエストするところ)
268
+ ┃ ┗CropUtils.js (画像生成処理など)
269
+ ┠ pages
270
+ ┃ ┗accountSettings
271
+ ┃ ┗ImageSettings.js (setProfileIconの呼び出し元)
272
+
37
273
 
38
274
  ### 試したこと
39
275
 

1

タイトル修正

2023/01/06 21:55

投稿

whitehackerh
whitehackerh

スコア2

test CHANGED
@@ -1 +1 @@
1
- Laravelでmultipart/form-dataのリクエストが受け取れない
1
+ Laravelでmultipart/form-data(画像)のリクエストが受け取れない
test CHANGED
File without changes