axios๋ฅผ ์ฌ์ฉํ์ฌ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ค๊ณ ์๋ํ์ง๋ง ํ ์ ์์ต๋๋ค. chrome dev ๋๊ตฌ์์๋ ์์ฒญ์ ์ฝํ
์ธ ๊ฐ ํ์๋์ง ์์ต๋๋ค. ์ฐํธ ๋ฐฐ๋ฌ๋ถ API ์๋ต์ ํตํด ๋์ผํ ์์ฒญ์ ์ ์์ ์ผ๋ก ์ํํฉ๋๋ค.
js
(newData) => {
const data = new FormData();
data.append('name', 'raphael');
data.append('file', {
uri: newData.image.path,
type: 'image/jpeg',
name: 'teste'
});
return axios.post(`${constants.development.URL_API}/file`, data,
headers: {
'Content-Type': 'multipart/form-data',
},
})`
@corujoraphael ํธ๊ธฐ์ฌ์, ์ด ์์ฒญ์ด ๋ ์ผ์ฐ ์๋ํ๋์?
@corujoraphael ๊ทํ์ ์ฝ๋๊ฐ ์๋ ๊ทธ๋๋ก ์คํ๋๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ํค๋๋ฅผ ์ง์ ํ๋ ๊ตฌ์ฑ์ ๊ฐ์ฒด์ฌ์ผ ํ์ง๋ง ์ค๊ดํธ๊ฐ ์์ต๋๋ค.
(newData) => {
const data = new FormData();
data.append('name', 'raphael');
data.append('file', {
uri: newData.image.path,
type: 'image/jpeg',
name: 'teste'
});
return axios.post(
`${constants.development.URL_API}/file`,
data,
{
headers: {
'Content-Type': 'multipart/form-data',
}
},
)
}
์ด ๋ฌธ์ ๋ ์๋ต์ ๋ฐ์ง ๋ชปํ๊ณ ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง๊ณ ์๋ ์ ๋ณด์ ๋ฐ๋ฅด๋ฉด Axios ๋ฒ๊ทธ์ฒ๋ผ ๋ณด์ด์ง ์์ผ๋ฏ๋ก ์ด ๋ฌธ์ ๋ฅผ ๋ซ์ต๋๋ค.
์ฝ๋ ๋๋ฒ๊น ์ ๋ํ ์ถ๊ฐ ์ง์์ด ํ์ํ๋ฉด Stack Overflow , Gitter ์ ๊ฒ์ํ๊ฑฐ๋ ์ด ๋ฌธ์ ์ ๋ํด ๋ค์ ๋๊ธ์
๊ฐ์ฌ ํด์!
@emilyemorehouse , React Native FormData๋ฅผ ์ฌ์ฉํ์ฌ ๋์ผํ ๋ฌธ์ ๋ฅผ ๋ง๋ฌ์ต๋๋ค.
let s = JSON.stringify({ uri: localUri, name: filename, type: type });
let formData = new FormData();
formData.append('ph0t0', s);
axios.post("http://10.0.1.2:8888/uploadphoto", {
method: "POST",
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8; boundary="another cool boundary";'
},
body: formData,
}).then((resp) => {
console.log(resp);
}).catch(err => {
console.log(err);
});
MultipartForm, Form ๋๋ PostForm์ ์ผ๋ถ ํค:๊ฐ ์์ด ์์ด์ผ ํฉ๋๋ค.
2018/02/20 18:25:31.740 [W] [photo.go:411] req.MultipartForm: nil
2018/02/20 18:25:31.740 [W] [photo.go:412] req.Form: map[]
2018/02/20 18:25:31.740 [W] [photo.go:413] req.
2018/02/20 18:25:31.740 [W] [photo.go:410] ์์ฒญ: &{POST /uploadphoto/dialog/57120e8951c643ab42a8c19f/0000000000000000000000 =utf-8] User-Agent:[okhttp/3.6.0] Accept:[application/json, text/plain, / ] Content-Length:[419] ์ฐ๊ฒฐ:[Keep-Alive] Accept-Encoding:[gzip] ์ฟ ํค:[lang=zh-TW; PVsessionID=db9a21d63b2d0ea47b68fa8755bd87e2]] 0xc420e3cb80
2018/02/20 18:25:31.740 [E] [photo.go:425] [UploadPhotos] ์ค๋ฅ: ์์ฒญ ์ฝํ
์ธ ์ ํ์ด multipart/form-data๊ฐ ์๋๋๋ค.
๋ฒ์ ์ ๋ณด
์ถ: 0.16.2
์์คํฌ: 25.0.0
๋ฐ์: 16.2.0
๋ฐ์ ๋ค์ดํฐ๋ธ: 0.52.0
์ด ๋ฒ๊ทธ๋ ์ต์ axios์์ ์ฌ์ ํ ๋ฐ์ํฉ๋๋ค. ์ ์ด๊ฒ์ด ๋ซํ์ต๋๊น?
๋๊ตฐ๊ฐ๊ฐ ์ฌํ ๊ฐ๋ฅํ ์๋ฅผ ์ ๊ณตํ ์ ์๋ค๋ฉด ์ด ๋ฐฑ์ ์ ๊ณต๊ฐํ๊ฒ ๋์ด ๊ธฐ์ฉ๋๋ค.
@ Ernie6711 ์ฝ๋์ ์ค๋ฅ๊ฐ ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์์ ๋ฐ์ดํฐ์ ์ถ๊ฐํ๊ธฐ ์ ์ ํ์ผ ๋ฐ์ดํฐ๋ฅผ axios.post(url[, data[, config]])
๋ค์์ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ์์ ๋๋ค.
const file ={ uri: localUri, name: filename, type: type};
const formData = new FormData();
formData.append('file', s);
const config = {
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8; boundary="another cool boundary";'
}
};
axios.post("http://10.0.1.2:8888/uploadphoto", formData, config).then((resp) => {
console.log(resp);
}).catch(err => {
console.log(err);
});
@emilyemorehouse wireshark ์ถ์ ์ ํ์ธํ์ญ์์ค. ๋ฐ์ดํฐ๊ฐ ์ ๋ก๋๋์ง๋ง ์ ์ ํ ์์ ๋ฐ์ดํฐ ์ธ์ฝ๋ฉ์ ์ฌ์ฉํ์ง ์์ต๋๋ค. ์์ ์๋ ๋ฌธ์ ๋ฅผ ์ฌํํ ์ ์์ต๋๋ค. ๋ค์ - ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์ ์ก๋์์ง๋ง ์ธ์ฝ๋ฉ์ด ์๋ชป๋์์ต๋๋ค.
POST /myserver HTTP/1.1
Content-Type: multipart/form-data
_parts=USERX%2Cusername&_parts=FILE1%2C%5Bobject%20Object%5D
React Native ํ๊ฒฝ ์ธ๋ถ์ ์์ง๋ง ์๋ํ๊ณ ์ฌ๋ฐ๋ฅด๊ฒ ์ธ์ฝ๋ฉ๋ ํ์ผ ์ ๋ก๋๋ฅผ ์ํ ๋ก์ปฌ ํด๋ผ์ด์ธํธ ๋ฐ ์๋ฒ ์์ ๊ฐ ์์ต๋๋ค. ์์ ์๋ ๋ด๊ฐ ์ก์ธ์คํ ์ ์๋ ๋ณ์์ ์๋ฒ์ ์์กดํ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์ฌํํ ์ ์์ต๋๋ค.
์ ๋ก๋์ ์๋ชป๋ ๊ฒ์ด ์์ผ๋ฉด ์์ ๋ฐ์ดํฐ ๊ฐ์ฒด์ ์ถ๊ฐ๋ ๋ฐ์ดํฐ๊ฐ ์ ํจํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
@emilyemorehouse ํ์ผ ์ ๋ก๋๋ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์๋ฒฝํ๊ฒ ์๋ํ๋ฉฐ ๊ธฐ๋ณธ ๋ฐ์์์๋ง ์คํจํฉ๋๋ค. ๋น์ ์ด ์์ ์ ์ธ๊ธํ๋ฏ์ด - ๋น์ ์ ํ ์คํธ๋ ๋ฐ์ ๋ค์ดํฐ๋ธ ํ๊ฒฝ ์ธ๋ถ์ ์์๊ณ , ๋๋ ๊ฑฐ๊ธฐ์์ ์์ํ ๊ฒ์ ๋๋ค :)
๋ฐ์ ๋ค์ดํฐ๋ธ์์ ๊ฐ๋จํ ํ์ผ ์ ๋ก๋๋ฅผ ์๋ํ๊ณ wireshark ์ถ์ ์ ํ์ธํ๊ธฐ๋ง ํ๋ฉด ์๋ํ๋ ์๋ฒ๊ฐ ํ์ํ์ง ์์ต๋๋ค...
๋๋ ๊ฐ์ ๊ฒ์ ์ป์์ต๋๋ค. Axios๋ ๋ค์ดํฐ๋ธ์ ๋ฐ์ํ์ง ์์ต๋๋ค.
@duongtranpyco ํ๋ก์ ํธ์์
์ถ์ JSON์ ์ฃผ๋ก ๋ณด๋ด๋ ๊ฒฝ์ฐ ์ฝ๋๋ฅผ ์์ ํ์ญ์์ค. ํ์์ ๊ฒฝ์ฐ ์๋ฒ๋ ์ผ๋ฐ์ ์ผ๋ก ์์์ ๊ธฐ๋ํฉ๋๋ค.
const request = async ({url, method = 'GET', params, body, responseType = 'json', headers = {}})=>{
const escape = (data, encode = encodeURIComponent)=>Object.keys(data||{}).reduce((pairs, key)=>{
for (let value of [].concat(data[key]))
pairs.push([`${key}`, `${value}`]);
return pairs;
}, []).map(pair=>pair.map(encode).join('=')).join('&');
if (Object.keys(params||{}).length)
url += '?'+escape(params);
if (method=='POST' && typeof body=='object')
{
if (body instanceof FormData)
headers['Content-Type'] = 'multipart/form-data';
else
{
body = escape(body);
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
}
let {statusCode, request: req} = await new Promise((resolve, reject)=>{
let xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.withCredentials = true;
xhr.responseType = {json: 'text'}[responseType]||responseType;
xhr.onload = ()=>resolve({statusCode: xhr.status, request: xhr});
xhr.onerror = ()=>reject(new TypeError('Network request failed'));
xhr.ontimeout = ()=>reject(new TypeError('Network request timed out'));
for (let key in headers)
xhr.setRequestHeader(key, headers[key]);
xhr.send(body||null);
});
if (statusCode<200 || statusCode>=400)
throw new Error(`network request failed with ${statusCode}: ${url}`);
switch(responseType)
{
case 'json':
return JSON.parse(req.responseText);
case 'text':
return req.responseText;
case 'request':
return req;
}
return req.response;
};
request.get = (url, opt = {})=>request({...opt, url, body: null});
request.post = (url, ...args)=>request({...args[1]||{}, url, method: 'POST', body: args[0]});
์๋ ํ์ธ์, ๋๋ ๊ฐ์ ๋ฌธ์ ์ ์ง๋ฉดํ๊ณ ์์ต๋๋ค. ์๋ฌด๋ ํด๊ฒฐ์ฑ ์ ์ฐพ์์ต๋๊น?
๋ ๋ฒ์งธ @giladno. ๋ค๋ฅธ ๋ถ๋ถ์ ์งํํ ๊ฒ์ด๋ฏ๋ก ๋์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ ๋ fetch()๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ํจ์๋ฅผ ์์ฑํฉ๋๋ค.
์ด์ ์ ์๋ํ์ง๋ง ๋ฐ์ดํฐ ๋๋ ์ธ์๋ฅผ ๋ฌธ์์ดํํ์ง ์์๋ ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ์๋ฒ๊ฐ ํธ์ถ์ ๋ฐ์์ง๋ง ๋ฐ์ดํฐ๊ฐ ์ ์ก๋์ง ์์์ต๋๋ค.
@ Ernie6711 ๋ถ๋ช ํ ํค๋๊ฐ ์์๋์ด ์๋ฒ๊ฐ ์์ ๋ฐ์ดํฐ๋ฅผ ์๋ฝํ ์ ์๋ ์ด์ ์ ๋๋ค. fetch()๋ก ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๊ตฌํํ๋๋ฐ ๋ฌธ์ ์์ด ์๋ํ์ต๋๋ค.
๋ฐ์ ๋ค์ดํฐ๋ธ์์ ์ฝ๊ฐ์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
๋ฌธ์ ๋ ํค๋๊ฐ ์๋๋๋ค.
์น์์ ์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
let formData = new FormData()
formData.append('key', true // bool value)
๊ทธ๋ฌ๋ ๋ฐ์ ๋ค์ดํฐ๋ธ์์ ํค์ ๊ฐ์ ๋ฌธ์์ด์ด์ด์ผ ํฉ๋๋ค.
formData.append('key', 'true')
@giladno๊ฐ ์ธ๊ธํ ๊ฒ์ฒ๋ผ URL์ ๊ฒ์ํ ๋ ๋ค์ดํฐ๋ธ ๋ฐ์์ ๋ํด ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ๋ฐ์ ๋ค์ดํฐ๋ธ์์ ํ ์คํธํด์ผ ํ๋ฉฐ ์ฌ๋๋ค์ด @emilyemorehouse๋ฅผ ๋ณด๊ณ ํ๋ค๋ ์ค๋ฅ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. 14์ผ ์ ์ ์ค๋ฅ๋ฅผ ๋ณด๊ณ ํ์ต๋๋ค. https://github.com/axios/axios/issues/1618 ํ์ง๋ง ์๋ฌด๋ ์๋ตํ์ง ์์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ readme์ ๋ฐ์ ๋ค์ดํฐ๋ธ์์ axios๋ฅผ ์ง์ํ์ง ์๋๋ค๊ณ ์ค๋ช ํ์ญ์์ค. ๋ง์ ๊ฐ๋ฐ์๋ค์ ์๊ฐ์ ๋ญ๋นํ์ง ์์ต๋๋ค..!
์ฌ๋ฌ๋ถ, ๋ฐฉํฅ์ ๊ณ ๋ง๊ฒ ์๊ฐํ์ง๋ง "React Native์์ ํ์ผ ์ ๋ก๋๋ฅผ ํ ์คํธํ๋ ๊ฒ"์ ๊ทธ๋ ๊ฒ ์ฌ์ด ์ผ์ด ์๋๋๋ค. ๋๋ React Native์์ ์์ฃผ ์ผํ์ง ์์ผ๋ฉฐ ํ ์คํธํ ํ์ผ ์ ๋ก๋๋ก ํ๋ก์ ํธ๋ฅผ ์์ํ๋ ๋ฐ ์๋นํ ์๊ฐ์ด ๊ฑธ๋ฆฝ๋๋ค.
React Native์์ ์๋ํ๋ ์์ ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฐ๋จํ POST๊ฐ ์์ต๋๋ค.
const formData = new FormData();
formData.append("data", true);
const config = {
headers: {
"Content-Type": "multipart/form-data; charset=utf-8;"
}
};
axios.post(URL, formData, config).then(
response => {
console.log({ response });
},
error => {
console.log({ error });
}
);
true
๋ ์๋ฒ๋ก ๋ณด๋ผ ๋ ๋ฌธ์์ดํ๋๋๋ฐ, ํฅ๋ฏธ๋กญ์ต๋๋ค.
์ฆ, ๋๊ตฌ๋ ์ง ๋ด๊ฐ ์ฝ๊ฒ ์คํ์ ํ ์ ์๋ ์์ ๋ฅผ ์ ๊ณตํ ์ ์๋ค๋ฉด ๋ ์์ธํ ์ดํด๋ณด๊ณ ์ถ์ต๋๋ค.
@corujoraphael ์ด๋ป๊ฒ ํด๊ฒฐํ์ จ๋์?
์ถ๊ฐํ๊ธฐ ์ง์ ์ "Image.type='image/png"
์ ๊ฐ์ ์ด๋ฏธ์ง ๊ฐ์ฒด ๋ด๋ถ์ ์ ํ์ ์ถ๊ฐํ๊ณ ์ฝํ
์ธ ์ ํ์ "Content-Type":"multipart/form-data"
.
image.type='image/png'
formData.append("resource", image);
return axios
.post('yourBAckendUrl', formData, {
headers: {
Authorization: "yourTokenHere",
"Content-Type": "multipart/form-data"
}
})
์ด๊ฒ์ด ๋๊ตฐ๊ฐ๋ฅผ ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ด ์์ ๋ ์ํํ ์ ์์์ต๋๋ค. multipart/form-data๋ฅผ S3(๋น๋์ค)์ PUTํ๋ ค๊ณ ํฉ๋๋ค. ์ ํ ์ต์ ์ ์ฌ์ฉํด๋ ๋์์ด ๋์ง ์์์ต๋๋ค. multipart/form-data ํค๋๊ฐ PUT ์์ฒญ์ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
const data = new FormData();
data.append('video', {
uri: 'file:///<path_to_video_on_iphone.mov',
type: 'video/quicktime',
name: 'video.mov',
});
response = yield call(() => {
return api.put(url, data, {
headers: {
'Content-Type': 'multipart/form-data',
},
transformRequest: [
(data, headers) => {
delete headers.common.Authorization;
return data;
}
]
});
});
์ด ๊ฒฝ์ฐ fetch๋ฅผ ์ฌ์ฉํ์ญ์์ค :D
var data = new FormData();
data.append('my_photo', {
uri: filePath, // your file path string
name: 'my_photo.jpg',
type: 'image/jpg'
}
fetch(path, {
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
method: 'POST',
body: data
});
axios ๋ฒ์ 0.18.0
์์ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
axios๋ FormData
๋ฐ๋๋ฅผ ์์ฑํ ๋ post, put
๋ฑ์ ๋ฐ๋๊ฐ ์์ผ๋ฉด ๋ด๋ถ์ ์ผ๋ก Content-Type
ํค๋๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
์ด๊ฒ์ ๋ด ์ฝ๋์
๋๋ค:
const sendFormData = images => {
// images is an array of base64 images (strings)
const formData = new FormData();
images.forEach((img, i) => {
formData.append('file', img);
formData.append('name', `${type}-${i + 1}`);
});
config.headers = {
.'Content-Type': 'multipart/form-data'
};
return axios.post(`http://someurl.com/api/upload-images`, formData, config)
.then(response => {
// handle success
})
.catch(err => {
// handle err
});
}
๋ฆฌ์กํธ ๋ค์ดํฐ๋ธ ๋ฒ์ 0.55.3
์ด ์์ ์ฝ๋๋ ์๋ํ์ง ์์ต๋๋ค. ์์ฒญ ๋ณธ๋ฌธ์ด ๋น์ด ์๊ณ Content-Type
ํค๋๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค.
๋์ผํ ๋ฌธ์ ๊ฐ ์๊ณ ๋๋ฒ๊ทธ๋ฅผ ํ์ธํ ๋ ๋ด ๋ชธ์ ๋ฌธ์์ด์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ ๋ด ์ฝ๋
const formData = new FormData();
formData.append('photo', {
uri: response.uri.replace('file://', ''),
mineType: 'image/jpeg',
fileType: 'image/jpg',
type: 'image/jpg',
name: 'test.jpg'
});
console.log('form data', formData);
Axios({
method: 'post',
url: 'https://dev-host.luxstay.net/api/rooms/10740/photos',
data: formData,
headers: {
Authorization: token,
'Content-Type': 'multipart/form-data'
}
});
์ ๋ฐ์ดํธ : ์ฑ์ด ๋๋ฒ๊ทธ ๋ชจ๋์ ์์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋๋ฒ๊ทธ ๋ชจ๋๋ฅผ ๋๋ฉด ์๋ํ์ต๋๋ค.
axios์๋ xhr ์ด๋ํฐ์ ์ฝ๋๊ฐ ์์ต๋๋ค.
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
๋๋ ๋ฐ์ ๋ค์ดํฐ๋ธ์์ ์ฝ๋์์ ์ค์ ํ๋๋ผ๋ ๋ค์ค ๋ถ๋ถ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ๊ฒฝ์ฐ ์ฝํ
์ธ ์ ํ ํค๋๋ฅผ ์ญ์ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ทธ๋ฌ๋ ์ค์ ์ฃผ์ ์ฒ๋ฆฌํ๋ฉด ๋ฐ์ ๋ค์ดํฐ๋ธ์์ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋ ์ง ํ์ ์ด ์์ง ์์ต๋๋ค.
axios ๋ฒ์
0.18.0
์์ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
axios๋FormData
๋ฐ๋๋ฅผ ์์ฑํ ๋post, put
๋ฑ์ ๋ฐ๋๊ฐ ์์ผ๋ฉด ๋ด๋ถ์ ์ผ๋กContent-Type
ํค๋๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
์ด๊ฒ์ ๋ด ์ฝ๋์ ๋๋ค:const sendFormData = images => { // images is an array of base64 images (strings) const formData = new FormData(); images.forEach((img, i) => { formData.append('file', img); formData.append('name', `${type}-${i + 1}`); }); config.headers = { .'Content-Type': 'multipart/form-data' }; return axios.post(`http://someurl.com/api/upload-images`, formData, config) .then(response => { // handle success }) .catch(err => { // handle err }); }
๋ฆฌ์กํธ ๋ค์ดํฐ๋ธ ๋ฒ์
0.55.3
์ด ์์ ์ฝ๋๋ ์๋ํ์ง ์์ต๋๋ค. ์์ฒญ ๋ณธ๋ฌธ์ด ๋น์ด ์๊ณContent-Type
ํค๋๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค.
๊ทธ๋ผ ์ด๋ค ๋ชจ์ต์ด์ด์ผ ํ ๊น์? ๊ฐ์ฒด ๊ฐ์ฒด๋ ์์ต๋๋ค.
@tkserver ๊ฐ์ฒด๊ฐ ์๊ณ toString()ํ ๋์ ๊ฐ์ต๋๋ค.
const a = {
name: 'dasdasd'
}
console.log(a.toString());
@tkserver ๊ฐ์ฒด๊ฐ ์๊ณ toString()ํ ๋์ ๊ฐ์ต๋๋ค.
const a = { name: 'dasdasd' } console.log(a.toString());
์๋ง๋ ๋๋ ๋ช ํํ์ง ์์์ต๋๋ค. ํค๋๋ฅผ ๋ณผ ๋ ์์ฒญ ํ์ด๋ก๋์ [๊ฐ์ฒด ๊ฐ์ฒด]๊ฐ ํ์๋์ด์ผ ํฉ๋๊น?
@tkserver ๋๋ฒ๊ฑฐ์ ๋คํธ์ํฌ ๊ฒ์ฌ๊ฐ ํ์ฑํ๋์ด ์์ผ๋ฉด ๋ฐ์ ๋ค์ดํฐ๋ธ์ FormData๋ฅผ ์ฌ์ ์ํ๊ณ ๋คํธ์ํฌ ๊ฒ์ฌ์์ [๊ฐ์ฒด ๊ฐ์ฒด]์ฒ๋ผ ๋ณด์ด๋ ๋น FormData ๊ฐ์ฒด๋ฅผ ์ ๋ฌํฉ๋๋ค.
๋คํธ์ํฌ ๊ฒ์ฌ๋ฅผ ๋๊ณ ์์ฒญ์ ํด๋ณด์ธ์. ๋์์ด ๋๊ธธ ๋ฐ๋๋๋ค.
Aman725, minhphung210 ๋ฑ ๋ชจ๋์๊ฒ ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ์ค์ ๊ธฐ๊ธฐ์์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋ก๋๊ฐ ์ ๋๋ก ์๋ํฉ๋๋ค. ์ด๊ฒ์ iOS ์๋ฎฌ๋ ์ดํฐ์ ๊ด๋ จ์ด ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ด ๋ฌธ์ ๊ฐ ์์์ง๋ง ์ง๊ธ์ ํด๊ฒฐ๋์์ต๋๋ค.
๊ทธ ์ด์ ๋ ๋คํธ์ํฌ ์์ฒญ์ ๋๋ฒ๊น ํ๊ณ ์์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ด ์ฑ์ ์ง์ ์ ์ ๋ค์ ์ฝ๋ ์ค์ด ์์ต๋๋ค.
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
๋๋ ๊ทธ๊ฒ์ ๋๊ธ๋ก ๋ฌ์๊ณ ์ง๊ธ ์๋ํฉ๋๋ค.
// IMPORTANT: this will cause FormData requests to fail.
// GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
๋ฐฉ๊ธ ์ฐพ์์ต๋๋ค: https://stackoverflow.com/a/47630754/387912 , ๋์์ด ๋๋์?
XMLHttpRequest
์์ฒญ์ด ๋ค์๊ฐ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ ๋ก๋๋๋๋ก ์ด๋ป๊ฒ ์์ฑํ๋์ง ์๋ ค์ฃผ์ค ์ ์์ต๋๊น? ๋คํธ์ํฌ ์ค๋ฅ๊ฐ ๊ณ์ ๋ฐ์ํฉ๋๋ค.
Error: Network Error
at createError (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:168963:17)
at XMLHttpRequest.handleError (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:168871:16)
at XMLHttpRequest.dispatchEvent (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:28708:27)
at XMLHttpRequest.setReadyState (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:28461:20)
at XMLHttpRequest.__didCompleteResponse (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:28288:16)
at blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:28398:47
at RCTDeviceEventEmitter.emit (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:3291:37)
at MessageQueue.__callFunction (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:2588:44)
at blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:2345:17
at MessageQueue.__guard (blob:http://localhost:8081/0754d453-361d-48d1-aaa5-9ff1ad808293:2542:13)
์ ๋ฐ์ดํธ: ์ง๊ธ ์๋ํ๋๋ก ๊ด๋ฆฌํ์ญ์์ค. ํ์ฌ ์ฌ์ฉ ์ค:
formData.append('picture', {
uri: data.uri,
name: fname,
type: 'image/' + ext
});
์ฌ๋๋ค์ด ์ฌ์ ํ ์ด ๋ฌธ์ ๋ฅผ ๊ฒช๊ณ ์๊ณ ๊ทํ๋
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
๋คํธ์ํฌ ์์ฒญ์ ๋๋ฒ๊ทธํ๊ธฐ ์ํ ์ค๋ํซ, ์ถ๊ฐ ์๋
GLOBAL.FormData = GLOBAL.originalFormData || GLOBAL.FormData
๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์ผํ์ต๋๋ค.
์ถ๊ฐํ๊ธฐ ์ง์ ์
"Image.type='image/png"
์ ๊ฐ์ ์ด๋ฏธ์ง ๊ฐ์ฒด ๋ด๋ถ์ ์ ํ์ ์ถ๊ฐํ๊ณ ์ฝํ ์ธ ์ ํ์"Content-Type":"multipart/form-data"
.image.type='image/png' formData.append("resource", image); return axios .post('yourBAckendUrl', formData, { headers: { Authorization: "yourTokenHere", "Content-Type": "multipart/form-data" } })
์ด๊ฒ์ด ๋๊ตฐ๊ฐ๋ฅผ ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋น์ ์ ๋์์ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! iOS Simunator๋ก ๋ด ์ฌ์ง์ ์ ๋ก๋ํ ์ ์์์ง๋ง Android Simunator๋ก๋ ์ ๋ก๋ํ ์ ์์์ต๋๋ค.
์ด๋ฏธ์ง ์ ํ์ 'image/jpeg'๋ก ์ค์ ํ ํ ๋คํธ์ํฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์์์ต๋๋ค.
const formData = new FormData();
formData.append('file', {
uri: pictureUri,
type: 'image/jpeg',
name: 'profile-picture'
})
์ถ๊ฐํ๊ธฐ ์ง์ ์
"Image.type='image/png"
์ ๊ฐ์ ์ด๋ฏธ์ง ๊ฐ์ฒด ๋ด๋ถ์ ์ ํ์ ์ถ๊ฐํ๊ณ ์ฝํ ์ธ ์ ํ์"Content-Type":"multipart/form-data"
.image.type='image/png' formData.append("resource", image); return axios .post('yourBAckendUrl', formData, { headers: { Authorization: "yourTokenHere", "Content-Type": "multipart/form-data" } })
์ด๊ฒ์ด ๋๊ตฐ๊ฐ๋ฅผ ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋น์ ์ ๋์์ ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! iOS Simunator๋ก ๋ด ์ฌ์ง์ ์ ๋ก๋ํ ์ ์์์ง๋ง Android Simunator๋ก๋ ์ ๋ก๋ํ ์ ์์์ต๋๋ค.
์ด๋ฏธ์ง ์ ํ์ 'image/jpeg'๋ก ์ค์ ํ ํ ๋คํธ์ํฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์์์ต๋๋ค.
const formData = new FormData(); formData.append('file', { uri: pictureUri, type: 'image/jpeg', name: 'profile-picture' })
์ด๊ฒ์ด ๋์์ด ๋ ์ ์๋ ๋ชจ๋ ์ฌ๋์๊ฒ ์ ์ก๋ ์ฝํ
์ธ ์ ํ์ ๋ชจ๋ฅด๋ ๊ฒฝ์ฐ multipart/form-data
. ์ง๊ธ ์ด๊ฒ์ ์ฝ๋ ๊ฒ์ด ๋น์ฐํ๊ฒ ๋ค๋ฆฌ์ง๋ง ๋ฏธ๋์ด๊ฐ ์๋ํ๋๋ก ํ๋ ๋ฐ ์ข์ ์๊ฐ์ด ๊ฑธ๋ ธ์ต๋๋ค.
data.append("image", {
name: "some_name", // also, won't work without this. A name is required
height: image.height,
width: image.width,
type: "multipart/form-data", // <-- this part here
uri:
Platform.OS === "android" ? image.uri : image.uri.replace("file:/", "")
});
์๋ ํ์ธ์, Vue.Js์์ Axios ์ ๋ก๋ ํ์ผ์ ์์ฑํ๋ ค๊ณ ํฉ๋๋ค. ๋๊ตฌ๋ ์ง ๋ฌธ์ ๋ฅผ ๊ฒํ ํ๊ณ ์ค๋ช ํ ์ ์์ต๋๊น?
HTML์์
<input type="file" id="file" ref="file" multiple @change="assignFiles"/>
Js์์
` assignFiles(e){
let uploadedFiles = this.$refs.file.files;
for( var i = 0; i < uploadedFiles.length; i++ ) {
this.form.AttachmentDocs.push(uploadedFiles[i]);
}
console.log(this.form.AttachmentDocs);
},`
๋ฐ๋ผ์ ๊ฒ์๋ฌผ์ ํธ๋ฆฌ๊ฑฐํ๋ ๋์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
`procedSave(e) {
e.preventDefault();
this.$v.$touch();
if(this.$v.$invalid) {
return;
}
this.showLoading(this.loaderId,true);
this.postDatas.append('form',JSON.stringify(this.form));
for( var i = 0; i < this.form.AttachmentDocs.length; i++ ){
let attachment = this.form.AttachmentDocs[i];
this.postDatas.append('files['+i+']',attachment,attachment.name);
}
}`
๊ทธ๋์ ๊ฒ์๋ฌผ์์ ์ฝ์ ์ฐฝ ํค๋๋ฅผ ํ์ธํ์ต๋๋ค.
ํ์ผ[0]: (๋ฐ์ด๋๋ฆฌ)
ํ์ผ[1]: (๋ฐ์ด๋๋ฆฌ)
ํ์ผ[2]: (๋ฐ์ด๋๋ฆฌ)
ํ์ผ[3]: (๋ฐ์ด๋๋ฆฌ)
๋ด ์๋ฒ ์ธก์์ ํ์ผ์ ๊ฐ์ ธ์ต๋๋ค.
์ ๊ด๋ จ์ด ์์ ์ ์์ต๋๋ค
this.postDatas.append('files['+i+']',attachment,attachment.name);
files['+' + i + '+']
์๋
๊ฐ์ ธ์ค๊ธฐ API์ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. req.body๋ ๋ ธ๋ js์์ ๋น์ด ์์ต๋๋ค. 'express-formidable'์ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค. https://www.npmjs.com/package/express-formidable
์ ๊ด๋ จ์ด ์์ ์ ์์ต๋๋ค
this.postDatas.append('files['+i+']',attachment,attachment.name);
files['+' + i + '+']
์๋
Sry + ๋ ์ฐ๊ฒฐ์ ๋ํ๋ด๊ณ wat ๋ ๋ฌธ์์ด '+' ์ ์ฌ์ฉ์ ๋ํ๋ ๋๋ค. ๊ฒฐ๊ณผ๋ ํ์ผ[+0+]์ด ๋ฉ๋๋ค.
๊ฐ์ ธ์ค๊ธฐ API์ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. req.body๋ ๋ ธ๋ js์์ ๋น์ด ์์ต๋๋ค. 'express-formidable'์ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค. https://www.npmjs.com/package/express-formidable
app.use(express.json({ extended: false }));
๋ฅผ ํฌํจํ์ต๋๊น?
์ ๊ด๋ จ์ด ์์ ์ ์์ต๋๋ค
this.postDatas.append('files['+i+']',attachment,attachment.name);
files['+' + i + '+']
์๋Sry + ๋ ์ฐ๊ฒฐ์ ๋ํ๋ด๊ณ wat ๋ ๋ฌธ์์ด '+' ์ ์ฌ์ฉ์ ๋ํ๋ ๋๋ค. ๊ฒฐ๊ณผ๋ ํ์ผ[+0+]์ด ๋ฉ๋๋ค.
ํ์ฌ ์ฝ๋๋ ํญ์ for ๋ฃจํ์์ files[+i+]
๋ฉ๋๋ค. ์ ๋ files[+0+], files[+1+], files[+2+], etc
๊ฐ ๋์ง ์์ต๋๋ค.
๋ํ files[+0+]
๋ ์ ๋ ์กด์ฌํ์ง ์์ต๋๋ค. ๋น์ ์ด ๋ฌด์์ํ๋ ค๊ณ ํ๋์ง ํ์คํ์ง ์์ต๋๋ค. files[0], files[1], files[2]...
ํ์ญ๋๊น? ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋ ค๊ณ ํ๋ค๋ฉด files['+0+']
๊ฐ์ง ์ ์์ง๋ง, ๋ค์ ๋งํ์ง๋ง, ๋ฌด์์ ์ฑ์ทจํ๋ ค๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
๊ฐ์ ธ์ค๊ธฐ API์ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. req.body๋ ๋ ธ๋ js์์ ๋น์ด ์์ต๋๋ค. 'express-formidable'์ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค. https://www.npmjs.com/package/express-formidable
app.use(express.json({ extended: false }));
๋ฅผ ํฌํจํ์ต๋๊น?
์๋์. ๊ทธ๋ฌ๋ ๊ฐ๋ ฅํ ํ ๊ฐ์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ ๋ก๋๋ ์ฌ์ง์ ํ๋๋ฅผ ์์ฒญํ๋ ค๊ณ ํฉ๋๋ค. ์์ฒญ ํ์ผ์ ์๋ฌด๊ฒ๋ ์์ต๋๋ค. ์ ์ด๋ฐ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ํ์คํ์ง ์์ต๋๋ค.
@duongtranpyco ํ๋ก์ ํธ์์
์ถ์ JSON์ ์ฃผ๋ก ๋ณด๋ด๋ ๊ฒฝ์ฐ ์ฝ๋๋ฅผ ์์ ํ์ญ์์ค. ํ์์ ๊ฒฝ์ฐ ์๋ฒ๋ ์ผ๋ฐ์ ์ผ๋ก ์์์ ๊ธฐ๋ํฉ๋๋ค.
const request = async ({url, method = 'GET', params, body, responseType = 'json', headers = {}})=>{ const escape = (data, encode = encodeURIComponent)=>Object.keys(data||{}).reduce((pairs, key)=>{ for (let value of [].concat(data[key])) pairs.push([`${key}`, `${value}`]); return pairs; }, []).map(pair=>pair.map(encode).join('=')).join('&'); if (Object.keys(params||{}).length) url += '?'+escape(params); if (method=='POST' && typeof body=='object') { if (body instanceof FormData) headers['Content-Type'] = 'multipart/form-data'; else { body = escape(body); headers['Content-Type'] = 'application/x-www-form-urlencoded'; } } let {statusCode, request: req} = await new Promise((resolve, reject)=>{ let xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.withCredentials = true; xhr.responseType = {json: 'text'}[responseType]||responseType; xhr.onload = ()=>resolve({statusCode: xhr.status, request: xhr}); xhr.onerror = ()=>reject(new TypeError('Network request failed')); xhr.ontimeout = ()=>reject(new TypeError('Network request timed out')); for (let key in headers) xhr.setRequestHeader(key, headers[key]); xhr.send(body||null); }); if (statusCode<200 || statusCode>=400) throw new Error(`network request failed with ${statusCode}: ${url}`); switch(responseType) { case 'json': return JSON.parse(req.responseText); case 'text': return req.responseText; case 'request': return req; } return req.response; }; request.get = (url, opt = {})=>request({...opt, url, body: null}); request.post = (url, ...args)=>request({...args[1]||{}, url, method: 'POST', body: args[0]});
๋ด ํ๋ก์ ํธ์์ ์ด ์ฝ๋๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด ์ฝ๋๋ฅผ ์ดํดํ๋ ค๊ณ ํฉ๋๋ค. ํ ๊ฐ์ง ์ง๋ฌธ์ ์ด ์ค์ด ํ์ํฉ๋๊น?
([].concat(data[key])์ ๊ฐ์ ๋ณด์)? ๊ฐ๋จํ๊ฒ ์ธ ์ ์๋๋ฐ,
pair.push([ ${key}
, ${data[key]}
]);
์ฌ๋๋ค์ด ์ฌ์ ํ ์ด ๋ฌธ์ ๋ฅผ ๊ฒช๊ณ ์๊ณ ๊ทํ๋
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest;
๋คํธ์ํฌ ์์ฒญ์ ๋๋ฒ๊ทธํ๊ธฐ ์ํ ์ค๋ํซ, ์ถ๊ฐ ์๋
GLOBAL.FormData = GLOBAL.originalFormData || GLOBAL.FormData
๊ทธ๊ฒ์ ๋๋ฅผ ์ํด ์ผํ์ต๋๋ค.
๊ณณ์?
๋๋ ๋ฌธ์ ๊ฐ ์์๋ค. Android์ ํ์ผ ๊ฒฝ๋ก๋ก ์ธํด ๋ฐ์ํฉ๋๋ค.
const ๊ฒฝ๋ก = utils.isAndroid() ?
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ๋ฉด ์ ์๋ํฉ๋๋ค.
const formData = new FormData();
const path = utils.isAndroid() ? `file://${filePath}` : filePath;
formData.append('Voice', {
uri: path,
name: 'test',
type: 'audio/wav',
});
formData.append('Content-Type', 'audio/wav');
const headers = {
'Content-Type': 'multipart/form-data',
};
return this._sendRequest(url, 'POST', formData, headers);
data.append('file', {
uri: newData.image.path,
type: 'image/jpeg',
name: 'teste'
name:'teste.jpeg'
์ ๊ฐ์ ์ด๋ฆ์ ์ฌ์ฉํ๋ฉด ์๋ํฉ๋๋ค. ํ์ผ์ ํ์์ ์ง์ ํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์๋ํ์ง ์์ต๋๋ค.
'example.jpeg'๋ผ๋ ์ด๋ฆ์ผ๋ก ์์ ํ์ต๋๋ค.
๋ฉฐ์น ๋์ axios๋ก ๋น๋์ค๋ฅผ ์ ๋ก๋ํ๋ ค๊ณ ์๋ํ๋ค๊ฐ ๊ทธ๋ฅ ํฌ๊ธฐํ์ต๋๋ค. RNFetchBlob์ ํนํ Android์์ ํ์ผ์ ์ ๋ก๋ํ๋ ๋ฐ ๋ ๋์ ์ต์ ์ ๋๋ค. ์ด ์น์ ์ ๊ตฌํํ๊ธฐ ์ฝ์ต๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ด ๋ฒ๊ทธ๋ ์ต์ axios์์ ์ฌ์ ํ ๋ฐ์ํฉ๋๋ค. ์ ์ด๊ฒ์ด ๋ซํ์ต๋๊น?