2015-10-10 9 views
15

ファイルのアップロードは突然変異のようです。それはしばしば他のデータを伴います。しかし、それは大きなバイナリブロブなので、GraphQLがどのようにそれに対処できるかはわかりません。あなたはRelayで構築されたアプリにファイルアップロードをどのように統合しますか?React-Relayアプリでファイルアップロードをどのように行いますか?

答えて

5

見つけたan explanation in the docs。 Relay.Mutationをサブクラス化し、getFiles関数を実装できます。

また、express-graphqlはサーバ側でこれを処理する方法のテストケースにan exampleを提供します。

+0

あなたはそれをうまく動作させましたか?共有することはできますか? –

+0

しました。私はチャンスを得たときにこれをもっと詳細に更新します。 –

10

まず、フロントエンドコンポーネントにリレー更新を書き込む必要があります。

onDrop: function(files) { 
    files.forEach((file)=> { 
    Relay.Store.commitUpdate(
     new AddImageMutation({ 
     file, 
     images: this.props.User, 
     }), 
     {onSuccess, onFailure} 
    ); 
    }); 
}, 

そして、フロントエンドに変異を実装することにより、次のとおりです:このように

class AddImageMutation extends Relay.Mutation { 
    static fragments = { 
    images:() => Relay.QL` 
     fragment on User { 
     id, 
     }`, 
    }; 

    getMutation() { 
    return Relay.QL`mutation{ introduceImage }`; 
    } 

    getFiles() { 
    return { 
     file: this.props.file, 
    }; 
    } 

    getVariables() { 
    return { 
     imageName: this.props.file.name, 
    }; 
    } 

    getFatQuery() { 
    return Relay.QL` 
     fragment on IntroduceImagePayload { 
     User { 
      images(first: 30) { 
      edges { 
       node { 
       id, 
       } 
      } 
      } 
     }, 
     newImageEdge, 
     } 
    `; 
    } 

    getConfigs() { 
    return [{ 
     type: 'RANGE_ADD', 
     parentName: 'User', 
     parentID: this.props.images.id, 
     connectionName: 'images', 
     edgeName: 'newImageEdge', 
     rangeBehaviors: { 
     '': 'prepend', 
     }, 
    }]; 
    } 
} 

そして最後には、サーバー/スキーマのハンドラを実装します。

const imageMutation = Relay.mutationWithClientMutationId({ 
    name: 'IntroduceImage', 
    inputFields: { 
    imageName: { 
     type: new GraphQL.GraphQLNonNull(GraphQL.GraphQLString), 
    }, 
    }, 
    outputFields: { 
    newImageEdge: { 
     type: ImageEdge, 
     resolve: (payload, args, options) => { 
     const file = options.rootValue.request.file; 
     //write the image to you disk 
     return uploadFile(file.buffer, filePath, filename) 
     .then(() => { 
      /* Find the offset for new edge*/ 
      return Promise.all(
      [(new myImages()).getAll(), 
       (new myImages()).getById(payload.insertId)]) 
      .spread((allImages, newImage) => { 
      const newImageStr = JSON.stringify(newImage); 
      /* If edge is in list return index */ 
      const offset = allImages.reduce((pre, ele, idx) => { 
       if (JSON.stringify(ele) === newImageStr) { 
       return idx; 
       } 
       return pre; 
      }, -1); 

      return { 
       cursor: offset !== -1 ? Relay.offsetToCursor(offset) : null, 
       node: newImage, 
      }; 
      }); 
     }); 
     }, 
    }, 
    User: { 
     type: UserType, 
     resolve:() => (new myImages()).getAll(), 
    }, 
    }, 
    mutateAndGetPayload: (input) => { 
    //break the names to array. 
    let imageName = input.imageName.substring(0, input.imageName.lastIndexOf('.')); 
    const mimeType = input.imageName.substring(input.imageName.lastIndexOf('.')); 
    //wirte the image to database 
    return (new myImages()) 
    .add(imageName) 
    .then(id => { 
    //prepare to wirte disk 
     return { 
     insertId: id, 
     imgNmae: imageName, 
     }; 
    }); 
    }, 
}); 

あなたは私のレポhttps://github.com/bfwg/relay-gallery でそれらを見つけることができます上のすべてのコードのライブデモhttp://fanjin.computer

+0

あなたの答えに関連するコードを含めてください。スタックオーバーフローは、リンクの腐敗を防ぐために、答えの "コア"の外側のリンクに頼っています。関連するものだけを引用し、完全なリポジトリにリンクすることは完全にOKです。 – mech

+1

私は自分の答えを更新しました。 –

4

もあり、私は単にである、彼のblogからマルク・アンドレ・ジルーの調査結果を共有していますRails-具体的なので、私はより一般的なものにしようとし、@ニックによって提供された答えの詳細を提供します。

  • クライアント側のJavaScriptコード
  • サーバー側のサーバー固有のコード

クライアント側のJavaScriptコード

クライアント - :

は、2つの部分がありますサイドコードはさらに2つの部分で構成されています:

  1. Relay.Mutation(UploadFileMutation)を拡張ファイルをアップロードする突然変異

    // The actual mutation 
    class UploadFileMutation extends Relay.Mutation { 
        getFiles() { 
        return { 
         file: this.props.file, 
        }; 
        } 
    
        // ... Rest of your mutation 
    } 
    
  2. ファイルを選択するためのUIをレンダリングするように反応成分(FileUploader)が含まれ、そして突然変異を呼び出すコンポーネントアップロードに

    // A react component to upload a file 
    class FileUploader extends React.Component { 
        onSubmit() { 
        const name = this.refs.name.value; 
        const file = this.refs.fileInput.files.item(0); 
        Relay.Store.update(
         new UploadFileMutation({ 
         name: name, 
         file: file, 
         }) 
        ); 
        } 
    
        // ... Rest of React component, e.g., render() 
    } 
    

サーバー側のサーバー固有のコードを実行する

サーバ側コードはまた、2つの部分からなる:

  1. 部分は、MIMEマルチパート形式でアップロードされたファイルを検索する処理とGraphQLスキーマで定義された突然変異にそれを渡します。 NodeJSとRailsの例を提供します。これは他のサーバのためのソリューションを導き出すのに役立ちます。
  2. NodeJS Expressサーバについて

express-graqphlテストケースから抽出@Nickによって指摘されるように):

import multer from 'multer'; 

    var app = express(); 
    var graphqlHTTP = require('express-graphql'); 

    // Multer provides multipart form data parsing. 
    var storage = multer.memoryStorage(); 

    app.use(urlString(), multer({ storage }).single('file')); 

    // Providing the request, which contains the file MIME 
    // multipart as `rootValue` to enable it to 
    // be accessible from within Schema resolve functions. 
    app.use(urlString(), graphqlHTTP(req => { 
     return { 
     schema: YourMutationSchema, 
     rootValue: { request: req } 
     }; 
    })); 

同様に、非JSサーバに対して、例えば、RubyOnRails:

def create 
     query_string = params[:query] 
     query_variables = ensure_hash(params[:variables]) || {} 

     query = GraphQL::Query.new(
     YourSchema, 
     query_string, 
     variables: query_variables, 
     # Shove the file MIME multipart into context to make it 
     # accessible by GraphQL Schema Mutation resolve methods 
     context: { file: request.params[:file] } 
    ) 
  1. Mutationは、渡されたMIMEマルチパートを取得できます。
01 JavascriptをGraphQLスキーマの

:RailsのGraphQLスキーマの

var YourMutationSchema = new GraphQLSchema({ 
     query: new GraphQLObjectType({ 
     // ... QueryType Schema 
     }), 
     mutation: new GraphQLObjectType({ 
     name: 'MutationRoot', 
     fields: { 
      uploadFile: { 
      type: UploadedFileType, 
      resolve(rootValue) { 
       // Access file MIME multipart using 
       const _file = rootValue.request.file; 

       // ... Do something with file 
      } 
      } 
     } 
     }) 
    }); 

AddFileMutation = GraphQL::Relay::Mutation.define do 
     name "AddFile" 
     input_field :name, !types.String 

     # ... Add your standard mutation schema stuff here 

     resolve -> (args, ctx) { 
     # Retrieve the file MIME multipart 
     file = ctx[:file] 
     raise StandardError.new("Expected a file") unless file 

     # ... Do something with file 
     } 
    end 
+0

すばらしい答え! :) – bryce

1

リレー現代で、他の回答に追加するには、あなたからファイルを送信する方法に小さな変更がありましたクライアント。コンポーネントの

UploadFileMutation.js

// @flow 

import { commitMutation, graphql } from 'react-relay'; 

import type { Environment } from 'react-relay'; 
import type { UploadFileInput, UploadFileMutationResponse } from './__generated__/uploadFileMutation.graphql'; 

const mutation = graphql` 
    mutation UploadFileMutation($input: UploadFileInput!) { 
    UploadFile(input: $input) { 
     error 
     file { 
     url 
     } 
    } 
    } 
`; 

const getOptimisticResponse = (file: File | Blob) => ({ 
    UploadFile: { 
    error: null, 
    file: { 
     url: file.uri, 
    }, 
    }, 
}); 

function commit(
    environment: Environment, 
    { fileName }: UploadFileInput, 
    onCompleted: (data: UploadFileMutationResponse) => void, 
    onError:() => void, 
    uploadables, 
) { 
    return commitMutation(environment, { 
    mutation, 
    variables: { 
     input: { fileName }, 
    }, 
    optimisticResponse: getOptimisticResponse(uploadables.fileToUpload), 
    onCompleted, 
    onError, 
    uploadables, 
    }); 
} 

export default { commit }; 

使用法:

代わりにあなたの突然変異で getFilesを有し、コンストラクタにファイルを渡すのは、次のようなものを使用することができます
const uploadables = { 
    fileToUpload: file, // file is the value of an input field for example 
}; 

UploadFileMutation.commit(
    this.props.relay.environment, 
    { fileName }, 
    onCompleted, 
    onError, 
    uploadables 
); 

uploadables設定オプションは隠されていますが、ドキュメントには言及されていませんが、ここにあります:https://github.com/facebook/relay/blob/c4430643002ec409d815366b0721ba88ed3a855a/packages/relay-runtime/mutations/commitRelayModernMutation.js#L32

関連する問題