How to Upload Multiple Files React Native

Khi lập trình ứng dụng mobile, có khá nhiều thứ liên quan đến hình ảnh. Hướng dẫn sau đây sẽ giúp bạn có thể upload nhiều hình ảnh trong app React Native, tương tự Facebook hay instagram…

Chuẩn bị

Trong hướng dẫn này sử dụng api code dot.net core. Bạn có thể sử dụng ngôn ngữ khác như nodejs, php, java… Bạn có thể tham khảo thêm tại đây.

Trong React Native sử dụng 3 thư viện

npm install react-native-image-crop-picker'
npm install react-native-actionsheet
npm install axios

Các bạn tham khảo thêm ở github của thư viện để biết cách cấu hình chi tiết.

Code backend API

[HttpPost]
[Route("")]
public async Task<IActionResult> Post([FromForm]List<IFormFile> files)
{
    try
    {
        var today = DateTime.Today;
        var uploadPath = @"C:\web\files\files"; // Path.Combine(_hostingEnvironment.WebRootPath, "Content", "Files");
        uploadPath += $"\\{today.Year}\\{today.Month}".PadLeft(2, '0');
        if (!Directory.Exists(uploadPath))
        {
            Directory.CreateDirectory(uploadPath);
        }
        
        var fileUrls = new List<string>();
        foreach (var file in files)
        {
            if (file.Length > 0)
            {
                var fileExtension = Path.GetExtension(file.FileName);
                string fileName = $"{FileHelper.GenerateUniqueFileName}{fileExtension}";
                await file.CopyToAsync(stream);
                var fileUrl = $"files/{today.Year}/{today.Month}".PadLeft(2, '0') + "/" + fileName;
                fileUrls.Add(fileUrl);
            }
        }

        var fileItemResponse = new ResultDataObject
        {
            Data = fileUrls,
            Code = (int)ErrorCodeResultEnum.Ok,
            Message = "Ok"
        };
        return Ok(fileItemResponse);
    }
    catch (Exception ex)
    {
        var fileItemResponse = new ResultDataObject
        {
            Message = ex.Message,
            Code = (int)ErrorCodeResultEnum.Failure
        };
        return Ok(fileItemResponse);
    }
}

Code React Native

Chọn ảnh từ thư viện sử dụng ImagePicker, sử dụng state để lưu lại ảnh đã chọn

onActionSelectPhotoDone = index => {
    switch (index) {
      case 0:
        ImagePicker.openCamera({}).then(image => {
          this.setState({
            localPhotos: [...this.state.localPhotos, image]
          });
        });
        break;
      case 1:
        ImagePicker.openPicker({
          multiple: true,
          maxFiles: 10,
          mediaType: 'photo'
        }).then(images => {
          images.forEach((image) => {
           this.setState({
            localPhotos: [...this.state.localPhotos, image]
          });
          });
        }).catch(error => {
          alert(JSON.stringify(error));
        });
        break;
      default:
        break;
    }
  };

Code hiển thị hình ảnh sau khi chọn

renderListPhotos = localPhotos => {
    const photos = localPhotos.map((photo, index) => (
      <TouchableOpacity key={index}
        onPress={() => {
           this.showActionSheet(index);
        }}
      >
        <Image style={styles.photo} source={{ uri: photo.path }} />
      </TouchableOpacity>
    ));

    return photos;
  };

  renderSelectPhotoControl = localPhotos => {
    return (
      <View style={styles.sectionContainer}>
        <Text style={styles.sectionTitle}>Select photos</Text>
        <ScrollView style={styles.photoList} horizontal={true}>
          {this.renderListPhotos(localPhotos)}
          <TouchableOpacity onPress={this.onPressAddPhotoBtn.bind(this)}>
            <View style={[styles.addButton, styles.photo]}>
              <Text style={styles.addButtonText}>+</Text>
            </View>
          </TouchableOpacity>
        </ScrollView>
      </View>
    );
  };

Sử dụng axios & FormData để Upload

  onDoUploadPress() {
    const { localPhotos } = this.state;
    if (localPhotos && localPhotos.length > 0) {
      let formData = new FormData();
      localPhotos.forEach((image) => {
        const file = {
          uri: image.path,
          name: image.filename || Math.floor(Math.random() * Math.floor(999999999)) + '.jpg',
          type: image.mime || 'image/jpeg'
        };
        formData.append('files', file);
      });

      axios
        .post('https://api.tradingproedu.com/api/v1/fileupload', formData)
        .then(response => {
          this.setState({ logs: JSON.stringify(response.data) });
        })
        .catch(error => {
          alert(JSON.stringify(error));
        });
    } else {
      alert('No photo selected');
    }
  }

Chúc các bạn thành công.

Full source code https://github.com/tuanitpro/react-native-upload-files

React Native – Create Login Form – Navigation

Chào các bạn

Tiếp tục tìm hiểu cái mới khi rảnh rỗi, ngoài những thứ quen thuộc, nên hôm nay mình tiếp tục thực hiện một App khó hơn bằng React Native. Đó là tạo form đăng ký, đăng nhập. Và một màn hình show ra những câu danh ngôn ý nghĩa.

Trong phần này có sử dụng nhiều phần, nên kiến thức hơi nhiều. Đầu tiên là API đăng ký đăng nhập, mình đã viết bằng MVC, có sử dụng token, mục đích Login, SignUp. Bạn có thể dùng API khác, tùy ý.

Phần thứ 2 là những câu danh ngôn, mình sử dụng API của PHP. Link: http://tuanitpro.com/android/api.php

Các kiến thức cần thiết khác của React Native đó là Navigation, component mới, version cũ sẽ không có, mà có tên là Navigator. Sử dụng để chuyển các màn hình. Ở ví dụ này đơn giản, nên không có truyền tham số khi chuyển trang.

AsyncStorage để lưu thông tin user đăng nhập.

Màn hình SplashScreen

 Code file android.index.js
/**
 * Project Login, SignUp
 * Author Name: Thanh Tuan Le
 * Author Email: [email protected]
 * Author URI: http://tuanitpro.com
 * Author FB: https://facebook.com/tuanitpro
 * Description:
 * This simple App make with love React Native
 * Download source code:
 * https://github.com/tuanitpro/react-native-weather-app
 * Donate: 
 * BTC Address: 1MXE6ZHSkNrZ2s6XYXtFhpgwRc8Hhuc9fr
 * Paypal: https://www.paypal.me/tuanitpro/5
 */

import React, { Component } from 'react';
import {
  AppRegistry,
 
} from 'react-native';
import {
  StackNavigator,
} from 'react-navigation';


 import MainPage from './MainPage.js';
 import SplashPage from './SplashPage.js';
 import LoginPage from './LoginPage.js';
 import ForgotPassPage from './ForgotPassPage.js';
 import SignUpPage from './SignUpPage.js';


const MyApp = StackNavigator({
  SplashPage: { screen: SplashPage },
  MainPage: { screen: MainPage },
  LoginPage: { screen: LoginPage },
  ForgotPassPage: { screen: ForgotPassPage },
  SignUpPage: {screen: SignUpPage}
});
AppRegistry.registerComponent('bookClientApi', () => MyApp);

Code Màn hình đăng ký

 const BASE_URL = "http://ec2-52-27-149-161.us-west-2.compute.amazonaws.com";

let serviceUrl = BASE_URL+  "/api/account/register";
      let userName = this.state.userName;
      let password = this.state.password;
      let confirmPassword = this.state.confirmPassword;
      // kiem tra o day  
    fetch(serviceUrl,{
        method: "POST",          
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
      body: JSON.stringify({
        'username': userName,
        'password': password,
        'confirmPassword':confirmPassword
      })



        })
          .then((response) => response.json())
          .then((responseJSON) => {  
                if(responseJSON.message=="Success"){
                     var { navigate } = this.props.navigation;
                     navigate('LoginPage');
                    ToastAndroid.show(responseJSON.message, ToastAndroid.SHORT)
                }
          })
          .catch((error) => {
            console.warn(error);
          });

Code Màn hình Login

const BASE_URL = "http://ec2-52-27-149-161.us-west-2.compute.amazonaws.com";

let serviceUrl =  BASE_URL + "/oauth2/token";
      let userName = this.state.userName;
      let password = this.state.password;
      var access_token = '';


      let postData = "grant_type=password&username=" + userName + "&password=" + password;              

        fetch(serviceUrl,{
          method: "POST",
          
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
              body: postData
        })
          .then((response) => response.json())
          .then((responseJSON) => {  
                
              
                 
               }
               else{
                  Alert.alert('Login failure');
               }
               
          })
          .catch((error) => {
            console.warn(error);
          });

Code màn hình chính

fetch('http://tuanitpro.com/android/api.php',{
          method: "GET",
           
            })
          .then((response) => response.json())
          .then((responseJSON) => {  
                 let dataArray = responseJSON;
               
              
                 var max = dataArray.length;
                 var min  = 1;
                 var randIndex = Math.floor(Math.random() * (max - min + 1)) + min;
                var item = dataArray[randIndex];
                if(item !=undefined){
                  this.setState({
                  quote : item.content,
                  author : item.author
                  });
                  
                }
                console.log(item);

          })
          .catch((error) => {
            console.warn(error);
          });

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
 ScrollView,
   AsyncStorage,
  TouchableOpacity,
   
} from 'react-native';
var STORAGE_KEY = 'key_access_token';
const background = require('./bg_quotes.jpg') ;

export default class MainPage extends Component {
   static navigationOptions = {
    title: 'Welcome',
    header:null,
  };
    constructor(props) {
    super(props);
  
    this.state = {
      quote: 'Cuộc sống không bao giờ là bế tắc thực sự nếu con người ta dám rời bỏ lối mòn & dũng cảm tìm ra giá trị mới.',
      author: 'Khuyết danh',
    };
  }
  
  componentWillMount() {
    try {
    AsyncStorage.getItem(STORAGE_KEY).then((user_data_json) => {
    let userData = JSON.parse(user_data_json);
        if(userData ==undefined){
              var { navigate } = this.props.navigation;
              navigate('LoginPage');
        }
this._onChangeText();
    });

    } catch (error) {
            console.log('AsyncStorage error: ' + error.message);
    }          

    
     
    }
    _onChangeText(event){
     fetch('http://tuanitpro.com/android/api.php',{
          method: "GET",
           
            })
          .then((response) => response.json())
          .then((responseJSON) => {  
                 let dataArray = responseJSON;
               
              
                 var max = dataArray.length;
                 var min  = 1;
                 var randIndex = Math.floor(Math.random() * (max - min + 1)) + min;
                var item = dataArray[randIndex];
                if(item !=undefined){
                  this.setState({
                  quote : item.content,
                  author : item.author
                  });
                  
                }
                console.log(item);

          })
          .catch((error) => {
            console.warn(error);
          }); 
    }
 _onPressLogOut (event) {
       try {
AsyncStorage.removeItem(STORAGE_KEY);
 
          var { navigate } = this.props.navigation;
          navigate('LoginPage');
} catch (error) {
        console.log('AsyncStorage error: ' + error.message);
}

       
    
 }

 render() {
    return (
        <Image style={[styles.container, styles.background]}
        source = {background}  resizeMode="cover">
        <View style={styles.container}>
 
          <View style={styles.wrapper}>
           <View style={styles.contents}>

 <TouchableOpacity activeOpacity={.5} onPress={this._onChangeText.bind(this)} keyboardShouldPersistTaps={true}>
            <Text style={styles.quotes}>{this.state.quote}</Text>
              <Text style={styles.author}>{this.state.author}</Text>

  </TouchableOpacity> 

      </View>


   </View>
   </View>

   <View style={styles.footer}>
        <TouchableOpacity activeOpacity={.5} onPress={this._onPressLogOut.bind(this)} keyboardShouldPersistTaps={true}>
    <View style={styles.button}>
<Text style={styles.buttonText}> Logout</Text>
      </View>      
      
    </TouchableOpacity> 
   
</View>
       </Image>
    );
  }
  
  
}
 
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
     
  },
  footer: {
  position: 'absolute',
  flex:1,
  left: 0,
  right: 0,
  bottom: -10,
 
},
  background:{
    width: null,
    height:null,
  },
  wrapper:{
      paddingHorizontal:15,
  },
  
button:{
backgroundColor:"#d73352",
paddingVertical: 8,
marginVertical:8,
alignItems: "center",
justifyContent: "center",
},


  buttonText: {
      fontSize: 16,
      color:'#FFFFFF',
      textAlign: 'center',
     
  },
  
  contents:{
        marginTop: 100,        
        backgroundColor: 'rgba(52, 52, 52, 0.3)'
  },
  quotes: {
     fontSize: 18,
    textAlign: 'center',
    color: '#FFFFFF',
    marginBottom: 5,
  },
  author: {
    textAlign: 'right',
    color: '#FFFFFF',
    marginBottom: 5,
  },
});

Các bạn có thể download mã nguồn tại  GitHub

Chúc các bạn thành công.

# Ứng dụng xem thời tiết đơn giản bằng React Native 

Weather app with react-native

Cuối tuần rảnh rỗi, tìm chút gì đó để làm, thoát ra những gì quen thuộc.

Chào các bạn.

React Native là một framework cho phép các lập trình viên xây dựng các ứng dụng trên nền tảng Android và iOS sử dụng ngôn ngữ lập trình javascript nhưng mang lại trải nghiệm native app thực sự.

Continue reading Weather app with react-native