【问题标题】:Rails API, how to receive photo that sent using FormData from react nativeRails API,如何接收使用 FormData 从 react native 发送的照片
【发布时间】:2020-11-20 12:46:35
【问题描述】:

我使用 rails 作为后端并将 react native 作为前端,我正在尝试使用 react native 中的 formdata 上传一张照片,并使用 rails 中的活动存储来保存它。 使用一个模型名称 Room.rb 和 has_one_attached :photo。

房间.rb

class Room < ApplicationRecord
  has_one_attached :photo
end

这里是rails收到的参数,有两个(房间名和照片)

{
  "room_name"=>"Guest Room", 
  "photo"=>
    <ActionController::Parameters {
      "uri"=>"file:///Users/MyName/Library/Developer/CoreSimulator/Devices/guest_room.jpg", 
      "name"=>"guest_room.jpg", 
      "type"=>"image/jpg"
    } permitted: true >
}

room_controller.rb 保存和接收文件如下

def create
  @room = Room.create(room_params)
  if @room.save
    render json: RoomSerializer.new(@room).serializable_hash, status: :created
  else
    render json: { errors: @room.errors }, status: :unprocessable_entity
  end
end

我在@room.save 中收到错误消息,提示“TypeError - 哈希键“uri”不是符号:' 我期望在我从手机(客户端)中选择图像并按下保存按钮后,它会自动下载图像,这也是我使用 react native 的 FormData 发送的原因。

更新 2:

这是上传照片的 react native 的一部分,

const preparePhoto = (uriPhoto) => {
  // ImagePicker saves the taken photo to disk and returns a local URI to it
  const localUri = uriPhoto;
  const name = localUri.split('/').pop();

  // Infer the type of the image
  const match = /\.(\w+)$/.exec(name);
  const type = match ? `image/${match[1]}` : `image`;

  return [name, type];
};

const createRoom = dispatch => async ({ room_name, uriPhoto }) => {

  const [name, type] = preparePhoto(uriPhoto);
  const photo = { uri: uriPhoto, name, type };
  const room = { room_name, photo };
  const formData = new FormData();

  formData.append('room', JSON.stringify(room));

  const config = { headers: {
    Accept: 'application/json',
    'Content-Type': 'multipart/form-data',
  } };

  try {
    const response = await serverApi.post('/rooms', formData, config);
    dispatch({ type: 'clear_error' });
  } catch (err) {
    console.log('error: ', err);
    dispatch({ type: 'add_error', payload: 'Sorry we have problem' });
  }
};

更新 3:

选择图像并将其发送到上下文的源代码

import React, { useState } from 'react';
import Constants from 'expo-constants';
import {
  ActivityIndicator,
  Button,
  Clipboard,
  Image,
  Share,
  StatusBar,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';


const RoomUploadPhoto = ({ uriPhoto, onPhotoChange }) => {
  const [uploading, setUploading] = useState(false);

  const renderUploadingIndicator = () => {
    if (uploading) {
      return <ActivityIndicator animating size="large" />;
    }
  };

  const askPermission = async (type, failureMessage) => {
    const { status, permissions } = await Permissions.askAsync(type);

    if (status === 'denied') {
      alert(failureMessage);
    }
  };

  const handleImagePicked = (pickerResult) => {
    onPhotoChange(pickerResult.uri);
  };

  const takePhoto = async () => {
    await askPermission(
      Permissions.CAMERA,
      'We need the camera permission to take a picture...'
    );
    await askPermission(
      Permissions.CAMERA_ROLL,
      'We need the camera-roll permission to read pictures from your phone...'
    );
    const pickerResult = await ImagePicker.launchCameraAsync({
      allowsEditing: true,
      aspect: [4, 3],
    });

    handleImagePicked(pickerResult);
  };

  const pickImage = async () => {
    await askPermission(
      Permissions.CAMERA_ROLL,
      'We need the camera-roll permission to read pictures from your phone...'
    );
    const pickerResult = await ImagePicker.launchImageLibraryAsync({
      allowsEditing: true,
      aspect: [4, 3],
    });

    handleImagePicked(pickerResult);
  };

  const renderControls = () => {
    if (!uploading) {
      return (
        <View>
          <View style={styles.viewSatu}>
            <Button
              onPress={pickImage}
              title="Pick an image from camera roll"
            />
          </View>
          <View style={styles.viewSatu}>
            <Button onPress={takePhoto} title="Take a photo" />
          </View>          
        </View>
      );
    }
  };

  return (
    <React.Fragment>
      <Text>upload photo</Text>
      {renderUploadingIndicator()}
      {renderControls()}

    </React.Fragment>
  );
};

const styles = StyleSheet.create({
  viewSatu: {
    marginVertical: 8
  }
});

export default RoomUploadPhoto;

【问题讨论】:

  • 可以上传你的 react 原生代码提交表单数据吗?
  • @yash 感谢您的回答,我添加了上传照片的反应原生代码(请参阅更新 2),如果您对反应原生部分有更正,请告诉我,因为这是我第一次尝试上传照片
  • 我可以看到您的请求不正确。您的照片应该是文件对象而不是 json。
  • 哦好的,能不能给我指正一下,谢谢
  • 您是否通过文件输入对话框上传文件?

标签: ruby-on-rails reactjs react-native


【解决方案1】:

确保将File 对象或base64 内容发布到后端。您的 photo 目前只是一个 json 对象,包含文件路径和名称。

请从您的room_params 中删除photo 参数。

def room_params
  params.require(:room).permit(
    :room_name
  )
end

并在您创建room 时附上您的photo

def create
  @room = Room.new(room_params)
  @room.attach params[:photo]
  ...

【讨论】:

猜你喜欢
  • 2020-03-16
  • 2016-11-03
  • 2020-05-01
  • 1970-01-01
  • 2015-11-24
  • 2016-07-09
  • 2020-08-18
  • 2022-08-19
  • 2017-04-07
相关资源
最近更新 更多