Как сделать пост-запрос API с изображением в Rails API с помощью скрепки от React - PullRequest
0 голосов
/ 26 декабря 2018

Я работаю над приложением, которое имеет клиентскую сторону React и серверную часть Ruby on Rails по отдельности (то есть я не использую гем «response_on_rails» или «Reaction-rails»).Я использую paperclip gem для обработки изображений в Rails.

У меня есть модель Post, которая имеет одно изображение и атрибуты, такие как следующие

# schema.rb
  create_table "posts", force: :cascade do |t|
    t.string "title"
    t.string "image"
    t.text "content"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "user_id"
    t.string "image_file_name"
    t.string "image_content_type"
    t.bigint "image_file_size"
    t.datetime "image_updated_at"
    t.boolean "published", default: false
    t.datetime "published_at"
    t.index ["user_id"], name: "index_posts_on_user_id"
  end

Здесь post.rb

class Post < ApplicationRecord
    has_attached_file :image

    validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/

    belongs_to :user
    validates :title, :image, :content, presence: true

    has_many :users_posts, through: :bookmarks
    has_many :users_posts, through: :user_like_posts
    has_many :users_posts, through: :try_and_likes

    has_many :comments

    default_scope {order(published_at: :desc)}
end

И вот новая форма публикации в компоненте React

import React, { Component } from 'react';
import { Form } from 'semantic-ui-react';
import CKEditor from "react-ckeditor-component";
import axios from 'axios';
import history from '../../history';
import { AuthWrapper } from '../../utils/auth_helpers';
import { getErrorMessages } from '../../utils/error_message_helpers';
import ErrorMessages from '../shared/ErrorMessages';

class NewPostForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            title: "", image: "", content: "", error_messages: [], client_image_url: ""
        }
        this.updateContent = this.updateContent.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.handleImageChange = this.handleImageChange.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.onChange = this.onChange.bind(this);
        this.onBlur = this.onBlur.bind(this);
    }

    handleChange(event) {
        this.setState({ [event.target.name]: event.target.value });
    }

    handleImageChange(event) {
        event.preventDefault();
        let image_url = window.URL.createObjectURL(event.target.files[0]);
        this.setState({image: image_url});
    }

    handleSubmit(event) {
        event.preventDefault();
        const user = this.props.user;
        if (event.target.name === "save") {
            axios.post("/api/v1/users/" + user.id + "/posts?published=false",
                {
                    post: {
                        title: this.state.title, image: this.state.image, content: this.state.content,
                        user_id: user.id
                    }
                })
                .then((result) => {
                    history.replace("/community");
                }).catch((err) => {
                    let errors = getErrorMessages(err);
                    this.setState({error_messages: errors});
                });
        } else if (event.target.name === "post") {
            axios.post("/api/v1/users/" + user.id + "/posts?published=true",
                {
                    post: {
                        title: this.state.title, image: this.state.image, content: this.state.content,
                        user_id: user.id
                    }
                })
                .then(() => {
                    history.replace("/community");
                }).catch((err) => {
                    let errors = getErrorMessages(err);
                    this.setState({error_messages: errors});
                });
        }
    }

    updateContent(newContent) {
        this.setState({
            content: newContent
        })
    }

    onChange(evt) {
        var newContent = evt.editor.getData();
        this.setState({
            content: newContent
        })
    }

    onBlur(evt) {}

    afterPaste(evt) {}

    render() {
        let image_field = null;
        if (this.state.image === "") {
            image_field = <div className="customized-file-input-container"><input id="image-file" type="file" name="image" onChange={this.handleImageChange} className="customized-file-input" accept="image/*" required></input><label for="image-file"><i className="fa fa-upload"></i> Choose a file</label></div>
        } else {
            image_field = <img className="uploaded-post-image" src={this.state.image} alt=""></img>
        }
        return (
            <div>
                <Form id="new-post-container">
                    <h1>New Post</h1>
                    <ErrorMessages error_messages={this.state.error_messages} />
                    <p>Title</p>
                    <input className="form-control" type="text" name="title" onChange={this.handleChange} required></input>
                    <CKEditor
                        activeClass="p10"
                        content={this.state.content}
                        events={{
                            "blur": this.onBlur,
                            "afterPaste": this.afterPaste,
                            "change": this.onChange
                        }}
                    />
                    <div className="new-post-bottom-container">
                        {image_field}
                        <div>
                            <input className="btn color-reverse-button contact-submit-button" type="submit" name="save" value="Save" onClick={this.handleSubmit}></input>
                            <input className="btn color-reverse-button contact-submit-button" type="submit" name="post" value="Post" onClick={this.handleSubmit}></input>
                        </div>
                    </div>
                </Form>
            </div>
        )
    }
}

const WrappedNewPostForm = AuthWrapper(NewPostForm);

export default WrappedNewPostForm;

Я использую createObjectURL() для отображения загруженного изображения на стороне клиента.Я попытался использовать URL-адрес blob: ...., сгенерированный для изображения, но этот выдал следующую ошибку:

Paperclip::AdapterRegistry::NoHandlerError (No handler found for "blob:http://localhost:3000/4c001255-53ad-45c0-8261-d706c5132383"):

Проблема в том, что URL-адрес должен начинаться с«http», но это начинается с «blob», как будто я удаляю часть «blob», она не будет отображать эту ошибку, но другая ошибка говорит, что она не может найти изображение.Кто-нибудь знает, как правильно сделать пост-запрос к Rails API с изображением, используя paperclip?

Дайте мне знать, если какая-то важная информация отсутствует.

1 Ответ

0 голосов
/ 09 мая 2019

Уведомление об устаревании

Настоятельно рекомендуется использовать ActiveStorage для обработки файлов на стороне сервера в Rails по таким причинам, как безопасность и поддержка.Paperclip теперь устарела с целью поощрения разработчиков к использованию ActiveStorage.Узнайте, почему здесь .

Благодаря Thoughtbot и Rails.

...