7-2. UploadPhoto Component

UploadPhoto
컴포넌트의 역할컴포넌트 코드
컨트랙트와의 상호작용
리덕스 스토어의 데이터 업데이트:
updateFeed
함수
1) UploadPhoto
component's role
UploadPhoto
component's roleUploadPhoto
컴포넌트는 Klaytn 블록체인에 사진을 업로드하는 요청을 처리합니다. 다음과 같은 과정을 거쳐 이루어집니다.
트랜잭션을 전송하여 스마트 컨트랙트의
uploadPhoto
메서드를 호출합니다.uploadPhoto
메서드에서 새로운 ERC-721 토큰이 발행됩니다.트랜잭션 전송 후
Toast
컴포넌트를 사용하여 트랜잭션 생명 주기로 진행 상황을 나타냅니다.트랜잭션이 블록에 담기면 로컬의 리덕스 스토어에서 새로운
PhotoData
로 업데이트합니다.
Limiting content size
The maximum size of a single transaction is 32KB
. So we restrict the input data (photo and descriptions) not to exceed 30KB
to send it over safely.
문자열 데이터의 크기는
2KB
로 제한됩니다.Photo is compressed to be less than
28KB
usingimageCompression()
function.
2) Component code
// src/components/UploadPhoto.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import imageCompression from 'utils/imageCompression';
import ui from 'utils/ui'
import Input from 'components/Input'
import InputFile from 'components/InputFile'
import Textarea from 'components/Textarea'
import Button from 'components/Button'
import * as photoActions from 'redux/actions/photos'
import './UploadPhoto.scss'
// Set a limit of contents
const MAX_IMAGE_SIZE = 30 * 1024 // 30KB
const MAX_IMAGE_SIZE_MB = 30 / 1024 // 30KB
class UploadPhoto extends Component {
state = {
file: '',
fileName: '',
location: '',
caption: '',
warningMessage: '',
isCompressing: false,
}
handleInputChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
handleFileChange = (e) => {
const file = e.target.files[0]
/**
* If image size is bigger than MAX_IMAGE_SIZE(30KB),
* Compress the image to load it on transaction
* cf. Maximum transaction input data size: 32KB
*/
if (file.size > MAX_IMAGE_SIZE) {
this.setState({
isCompressing: true,
})
return this.compressImage(file)
}
return this.setState({
file,
fileName: file.name,
})
}
handleSubmit = (e) => {
e.preventDefault()
const { file, fileName, location, caption } = this.state
this.props.uploadPhoto(file, fileName, location, caption)
ui.hideModal()
}
compressImage = async (imageFile) => {
try {
const compressedFile = await imageCompression(imageFile, MAX_IMAGE_SIZE_MB)
this.setState({
isCompressing: false,
file: compressedFile,
fileName: compressedFile.name,
})
} catch (error) {
this.setState({
isCompressing: false,
warningMessage: '* Fail to compress image'
})
}
}
render() {
const { fileName, location, caption, isCompressing, warningMessage } = this.state
return (
<form className="UploadPhoto" onSubmit={this.handleSubmit}>
<InputFile
className="UploadPhoto__file"
name="file"
label="Search file"
fileName={isCompressing ? 'Compressing image...' : fileName}
onChange={this.handleFileChange}
err={warningMessage}
accept=".png, .jpg, .jpeg"
required
/>
<Input
className="UploadPhoto__location"
name="location"
label="Location"
value={location}
onChange={this.handleInputChange}
placeholder="Where did you take this photo?"
required
/>
<Textarea
className="UploadPhoto__caption"
name="caption"
value={caption}
label="Caption"
onChange={this.handleInputChange}
placeholder="Upload your memories"
required
/>
<Button
className="UploadPhoto__upload"
type="submit"
title="Upload"
/>
</form>
)
}
}
const mapDispatchToProps = (dispatch) => ({
uploadPhoto: (file, fileName, location, caption) =>
dispatch(photoActions.uploadPhoto(file, fileName, location, caption)),
})
export default connect(null, mapDispatchToProps)(UploadPhoto)
3) Interact with contract
Klaytn에 사진 데이터를 쓰는 함수를 만들어보겠습니다. Send transaction to contract: uploadPhoto
Unlike read-only function calls, writing data incurs a transaction fee. 트랜잭션 수수료는 사용한 gas
의 양에 따라 결정됩니다. gas
는 트랜잭션을 처리하는 데에 필요한 계산량을 나타내는 측정 단위입니다.
따라서 트랜잭션을 보내려면 from
과 gas
두 속성이 있어야 합니다.
트랜잭션에 올릴 수 있도록 사진 파일을 바이트 문자열로 변환합니다.
(In Klaystagram contract, we defined photo fotmat as bytes in
PhotoData
struct)FileReader
를 사용하여 ArrayBuffer로 사진 데이터를 읽어옵니다.ArrayBuffer를 16진수 문자열로 변환합니다.
Add Prefix
0x
to satisfy bytes format
Invoke the contract method:
uploadPhoto
from
: An account that sends this transaction and pays the transaction fee.gas
: The maximum amount of gas that thefrom
account is willing to pay for this transaction.
After sending the transaction, show progress along the transaction lifecycle using
Toast
component.If the transaction successfully gets into a block, call
updateFeed
function to add the new photo into the feed page.
// src/redux/actions/photo.js
export const uploadPhoto = (
file,
fileName,
location,
caption
) => (dispatch) => {
// 1. Convert photo file as a hex string to load on transaction
const reader = new window.FileReader()
reader.readAsArrayBuffer(file)
reader.onloadend = () => {
const buffer = Buffer.from(reader.result)
// Add prefix `0x` to hexString to recognize hexString as bytes by contract
const hexString = "0x" + buffer.toString('hex')
// 2. Invoke the contract method: uploadPhoto
// Send transaction with photo file(hexString) and descriptions
try{
KlaystagramContract.methods.uploadPhoto(hexString, fileName, location, caption).send({
from: getWallet().address,
gas: '200000000',
}, (error, txHash) => {
if (error) throw error;
// 3. After sending the transaction,
// show progress along the transaction lifecycle using `Toast` component.
ui.showToast({
status: 'pending',
message: `Sending a transaction... (uploadPhoto)`,
txHash,
})
})
.then((receipt) => {
ui.showToast({
status: receipt.status ? 'success' : 'fail',
message: `Received receipt! It means your transaction is
in klaytn block (#${receipt.blockNumber}) (uploadPhoto)`,
link: receipt.transactionHash,
})
// 4. If the transaction successfully gets into a block,
// call `updateFeed` function to add the new photo into the feed page.
if(receipt.status) {
const tokenId = receipt.events.PhotoUploaded.returnValues[0]
dispatch(updateFeed(tokenId))
}
})
} catch (error) {
ui.showToast({
status: 'error',
message: error.toString(),
})
}
}
}
cf) Transaction life cycle
After sending transaction, you can get transaction life cycle (transactionHash
, receipt
, error
).
transactionHash
이벤트는 서명된 트랜잭션 인스턴스가 올바르게 구성되면 발생합니다. 네트워크에 트랜잭션을 전송하기 전에 트랜잭션 해시를 구할 수 있습니다.receipt
이벤트는 트랜잭션 영수증을 받을 때 발생합니다. 이는 트랜잭션이 블록에 들어갔음을 의미합니다.receipt.blockNumber
으로 블록 번호를 확인할 수 있습니다.error
이벤트는 어떤 문제가 있을 때 발생합니다.
4. 피드 페이지의 사진 업데이트: updateFeed
updateFeed
After successfully sending the transaction to the contract, FeedPage needs to be updated.
In order to update the photo feed, we need to get the new photo data we've just uploaded. tokenId
와 함께 getPhoto()
를 호출합니다. tokenId
는 트랜잭션 영수증에서 찾을 수 있습니다. 그다음 새 사진의 데이터를 로컬 리덕스 스토어에 저장하세요.
// src/redux/actions/photo.js
/**
* 1. Call contract method: getPhoto()
* To get new photo data we've just uploaded,
* call `getPhoto()` with tokenId from receipt after sending transaction
*/
const updateFeed = (tokenId) => (dispatch, getState) => {
KlaystagramContract.methods.getPhoto(tokenId).call()
.then((newPhoto) => {
const { photos: { feed } } = getState()
const newFeed = [feedParser(newPhoto), ...feed]
// 2. 리덕스 스토어에 새로운 피드를 업데이트합니다.
dispatch(setFeed(newFeed))
})
}
Last updated
Was this helpful?