7-2. Thành phần UploadPhoto

Chức năng của thành phần
UploadPhoto
Mã thành phần
Tương tác với hợp đồng
Cập nhật dữ liệu vào cửa hàng: hàm
updateFeed
1) Chức năng của thành phần UploadPhoto
UploadPhoto
Thành phần UploadPhoto
xử lý yêu cầu tải ảnh lên blockchain Klaytn. Quy trình như sau:
Gọi phương pháp
uploadPhoto
của hợp đồng thông minh bằng cách gửi một giao dịch. Trong phương pháp hợp đồnguploadPhoto
, một token ERC-721 mới được tạo.Sau khi gửi một giao dịch, hãy cho thấy tiến trình cùng vòng đời giao dịch bằng thành phần
Toast
.Khi giao dịch tiến vào một khối, hãy cập nhật
PhotoData
mới trong cửa hàng redux cục bộ.
Limiting content size
Kích thước tối đa của một giao dịch là 32KB
. Do đó, chúng tôi hạn chế kích thước của dữ liệu đầu vào (ảnh và mô tả) không vượt quá 30KB
để quá trình gửi đi diễn ra an toàn.
Quy mô dữ liệu chuỗi được giới hạn ở
2KB
Ảnh được nén để có kích thước nhỏ hơn
28KB
bằng hàmimageCompression()
.
2) Mã thành phần
// 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'
// Đặt giới hạn nội dung
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]
/**
* Nếu kích thước ảnh lớn hơn MAX_IMAGE_SIZE(30KB),
* Nén ảnh để tải trên giao dịch
* cf. Kích thước dữ liệu đầu vào giao dịch tối đa: 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: '* Nén ảnh không thành công'
})
}
}
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 ? "Đang nén ảnh..." : 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="Bạn đã chụp ảnh này ở đâu?"
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) Tương tác với hợp đồng
Hãy tạo một hàm để viết dữ liệu ảnh lên Klaytn. Send transaction to contract: uploadPhoto
Không giống các lệnh gọi hàm Read-only, việc viết dữ liệu làm phát sinh phí giao dịch. Phí giao dịch được xác định bằng lượng gas
đã sử dụng. gas
là đơn vị đo thể hiện số lượng phép tính cần để xử lý giao dịch.
Vì những lý do này, việc gửi một giao dịch cần hai thuộc tính from
và gas
.
Chuyển đổi tập tin ảnh thành một chuỗi byte để tải trên giao dịch
(Trong Klaystagram contract, chúng ta đã định nghĩa định dạng ảnh là byte trong cấu trúc
PhotoData
)Đọc dữ liệu ảnh dưới dạng ArrayBuffer bằng
FileReader
Chuyển đổi ArrayBuffer thành chuỗi số hex
Thêm tiền tố
0x
để thỏa mãn định dạng byte
Gọi phương pháp hợp đồng:
uploadPhoto
from
: Một tài khoản gửi giao dịch này và thanh toán phí giao dịch.gas
: Lượng gas tối đa mà tài khoảnfrom
sẵn sàng thanh toán cho giao dịch này.
Sau khi gửi giao dịch, hiển thị tiến trình cùng vòng đời giao dịch bằng thành phần
Toast
.Nếu giao dịch thành công tiến vào một khối, gọi hàm
updateFeed
để thêm ảnh mới vào trang nguồn cấp dữ liệu.
// src/redux/actions/photo.js
export const uploadPhoto = (
file,
fileName,
location,
caption
) => (dispatch) => {
// 1. Chuyển đổi tập tin ảnh thành chuỗi số hex để tải trên giao dịch
const reader = new window.FileReader()
reader.readAsArrayBuffer(file)
reader.onloadend = () => {
const buffer = Buffer.from(reader.result)
// Thêm tiền tố "0x" vào hexString để hợp đồng nhận diện hexString là byte
const hexString = "0x" + buffer.toString('hex')
// 2. Gọi phương pháp hợp đồng: uploadPhoto
// Gửi giao dịch với tập tin ảnh (hexString) và mô tả
try{
KlaystagramContract.methods.uploadPhoto(hexString, fileName, location, caption).send({
from: getWallet().address,
gas: '200000000',
}, (error, txHash) => {
if (error) throw error;
// 3. Sau khi gửi giao dịch,
// hiển thị tiến trình cùng vòng đời giao dịch bằng thành phần "Toast".
ui.showToast({
trạng thái: 'đang chờ',
message: `Gửi một giao dịch... (uploadPhoto)`,
txHash,
})
})
.then((receipt) => {
ui.showToast({
trạng thái: receipt.trạng thái ? 'success' : 'fail',
message: `Đã nhận biên lai! Điều đó nghĩa là giao dịch của bạn
ở trong khối klaytn (#${receipt.blockNumber}) (uploadPhoto)`,
link: receipt.transactionHash,
})
// 4. Nếu giao dịch của bạn thành công tiến vào một khối,
// gọi hàm "updateFeed" để thêm ảnh mới vào trang nguồn cấp dữ liệu.
if(receipt.trạng thái) {
const tokenId = receipt.events.PhotoUploaded.returnValues[0]
dispatch(updateFeed(tokenId))
}
})
} catch (error) {
ui.showToast({
trạng thái: 'error',
message: error.toString(),
})
}
}
}
cf) Vòng đời giao dịch
Sau khi gửi giao dịch, bạn có thể lấy vòng đời giao dịch (transactionHash
, receipt
, error
).
Sau khi phiên bản giao dịch đã ký của bạn được xây dựng đúng cách, sự kiện
transactionHash
sẽ được kích hoạt. Bạn có thể lấy hàm băm của giao dịch trước khi gửi giao dịch lên mạng lưới.Sự kiện
receipt
sẽ được kích hoạt khi bạn nhận được biên lai giao dịch. Điều đó nghĩa là giao dịch của bạn ở trong một khối. Bạn có thể xem số khối bằngreceipt.blockNumber
.Sự kiện
error
được kích hoạt khi có lỗi xảy ra.
4. Tải ảnh lên trang nguồn cấp dữ liệu: updateFeed
updateFeed
Sau khi thành công gửi giao dịch vào hợp đồng, FeedPage cần được cập nhật.
Để cập nhật nguồn cấp dữ liệu ảnh, ta cần lấy dữ liệu ảnh mới ta vừa tải lên. Hãy gọi getPhoto()
bằng tokenId
. tokenId
có thể được truy xuất từ biên lai giao dịch. Sau đó, thêm dữ liệu ảnh mới vào cửa hàng redux cục bộ.
// src/redux/actions/photo.js
/**
* 1. Gọi phương pháp hợp đồng: getPhoto()
* Để lấy dữ liệu ảnh mới ta vừa tải lên,
* gọi hàm "getPhoto()" bằng tokenId từ biên lai sau khi gửi giao dịch
*/
const updateFeed = (tokenId) => (dispatch, getState) => {
KlaystagramContract.methods.getPhoto(tokenId).call()
.then((newPhoto) => {
const { photos: { feed } } = getState()
const newFeed = [feedParser(newPhoto), ...feed]
// 2. cập nhật nguồn cấp dữ liệu mới vào cửa hàng
dispatch(setFeed(newFeed))
})
}
Last updated