文件上传似乎是一种变异。它通常伴随着其他数据。但它是一个很大的二进制blob,所以我不确定GraphQL如何处理它。如何将文件上传集成到使用Relay构建的应用程序中?
答案 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 还有一个现场演示https://fanjin.io
答案 1 :(得分:5)
我找到了an explanation in the docs。您可以继承Relay.Mutation并实现getFiles函数。
此外,express-graphql在其测试用例中提供an example如何在服务器端处理此问题。
答案 2 :(得分:4)
我只是从他的blog分享Marc-Andre Giroux的发现,这是Rails特有的,所以我会尝试使它更通用,并提供@Nick提供的答案的详细信息。
共有两部分:
客户端Javascript代码
客户端代码还包括两部分:
上传文件的变种,扩展了Relay.Mutation(UploadFileMutation)
// The actual mutation
class UploadFileMutation extends Relay.Mutation {
getFiles() {
return {
file: this.props.file,
};
}
// ... Rest of your mutation
}
包含React组件(FileUploader)的组件,用于呈现用于选择文件的UI,并调用突变来执行上载
// 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()
}
服务器端服务器专用代码
服务器端代码也包含两部分:
对于NodeJS Express服务器(从@Nick指出的express-graqphl测试用例中提取):
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] }
)
对于Javascript 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
}
}
}
})
});
对于Rails GraphQL架构:
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
答案 3 :(得分:1)
要使用Relay Modern添加其他答案,您应该如何从客户端发送文件进行一些小改动。您可以使用以下内容,而不是在变异中使用getFiles
并将文件传递给构造函数,而是使用以下内容:
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 };
组件用法:
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
答案 4 :(得分:0)
虽然您绝对可以实现将文件上传到GraphQL API端点,但它被认为是一种反模式(您会遇到最大文件大小等问题。)
更好的选择是从GraphQL API获取签名的URL,以将文件直接从客户端应用程序直接上传到Amazon S3,Google Cloud Storage等。
如果上传完成后服务器端代码需要将URL保存在数据库中,则可以直接预订此事件。以Google Cloud中的object change notification为例。