簡體   English   中英

如何使用graphql-java上傳文件?

[英]How to upload files with graphql-java?

如果我使用 graphql-java,我無法找到如何上傳文件,有人可以給我看演示嗎? 我將不勝感激!

參考: https : //github.com/graphql-java-kickstart/graphql-java-tools/issues/240

我在 springboot 中使用 graphql-java-kickstart graphql-java-tools 嘗試過,但是沒有用

@Component
public class FilesUpload implements GraphQLMutationResolver {

    public Boolean testMultiFilesUpload(List<Part> parts, DataFetchingEnvironment env) {
        // get file parts from DataFetchingEnvironment, the parts parameter is not used
        List<Part> attchmentParts = env.getArgument("files");
        System.out.println(attchmentParts);
        return true;
    }
}

這是我的架構

type Mutation {
    testSingleFileUpload(file: Upload): UploadResult
}

我希望這個解析器可以打印附件部分,所以我可以得到文件部分。

  1. 在我們的模式中定義一個標量類型

    scalar Upload

    我們應該為上傳配置 GraphQLScalarType,在下面使用它:

     @Configuration public class GraphqlConfig { @Bean public GraphQLScalarType uploadScalarDefine() { return ApolloScalars.Upload; } }
  2. 然后我們將為 testMultiFilesUpload 定義架構中的突變和 GraphQLMutationResolver

     type Mutation { testMultiFilesUpload(files: [Upload!]!): Boolean }

這是解析器:

public Boolean testMultiFilesUpload(List<Part> parts, DataFetchingEnvironment env) {
    // get file parts from DataFetchingEnvironment, the parts parameter is not use
    List<Part> attachmentParts = env.getArgument("files");
    int i = 1;
    for (Part part : attachmentParts) {
      String uploadName = "copy" + i;
      try {
        part.write("your path:" + uploadName);
      } catch (IOException e) {
        e.printStackTrace();
      }
      i++;
    }
    return true;   
  }
}
  1. javax.servlet.http.Part配置 jackson 反序列化器並將其注冊到 ObjectMapper

     public class PartDeserializer extends JsonDeserializer<Part> { @Override public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { return null; } }

    為什么我們返回null? 因為List<Part> parts總是 null ,在解析器的方法中,從 DataFetchingEnvironment 中獲取parts 參數;

    environment.getArgument("文件")

將其注冊到 ObjectMapper:

@Bean
public ObjectMapper objectMapper() {
  ObjectMapper objectMapper = new ObjectMapper();
  objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  SimpleModule module = new SimpleModule();
  module.addDeserializer(Part.class, new PartDeserializer());
  objectMapper.registerModule(module);
  return objectMapper;
}
  1. 為了測試這一點,將以下表單數據(我們使用 Postman)發布到 GraphQL 端點
operations { "query": "mutation($files: [Upload!]!) {testMultiFilesUpload(files:$files)}", "variables": {"files": [null,null] } } map { "file0": ["variables.files.0"] , "file1":["variables.files.1"]} file0 your file file1 your file

像這樣:

記得選擇表單數據選項在此處輸入圖片說明

通過這個我們可以上傳多個文件

主要問題是graphql-java-tools可能有問題來為包含非基本類型的字段(如ListStringIntegerBoolean等的解析器)進行字段映射...

我們通過創建我們自己的自定義標量來解決這個問題,它基本上類似於ApolloScalar.Upload 但是我們沒有返回Part類型的對象,而是返回我們自己的解析器類型FileUpload ,其中包含 contentType 作為String和 inputStream 作為byte[] ,然后字段映射起作用,我們可以在解析器中讀取byte[]

首先,設置要在解析器中使用的新類型:

public class FileUpload {
    private String contentType;
    private byte[] content;

    public FileUpload(String contentType, byte[] content) {
        this.contentType = contentType;
        this.content = content;
    }

    public String getContentType() {
        return contentType;
    }

    public byte[] getContent() {
        return content;
    }
}

然后我們創建一個看起來很像ApolloScalars.Upload的自定義標量,但返回我們自己的解析器類型FileUpload

public class MyScalars {
    public static final GraphQLScalarType FileUpload = new GraphQLScalarType(
        "FileUpload",
        "A file part in a multipart request",
        new Coercing<FileUpload, Void>() {

            @Override
            public Void serialize(Object dataFetcherResult) {
                throw new CoercingSerializeException("Upload is an input-only type");
            }

            @Override
            public FileUpload parseValue(Object input) {
                if (input instanceof Part) {
                    Part part = (Part) input;
                    try {
                        String contentType = part.getContentType();
                        byte[] content = new byte[part.getInputStream().available()];
                        part.delete();
                        return new FileUpload(contentType, content);

                    } catch (IOException e) {
                        throw new CoercingParseValueException("Couldn't read content of the uploaded file");
                    }
                } else if (null == input) {
                    return null;
                } else {
                    throw new CoercingParseValueException(
                            "Expected type " + Part.class.getName() + " but was " + input.getClass().getName());
                }
            }

            @Override
            public FileUpload parseLiteral(Object input) {
                throw new CoercingParseLiteralException(
                        "Must use variables to specify Upload values");
            }
    });
}

在解析器中,您現在可以從解析器參數中獲取文件:

public class FileUploadResolver implements GraphQLMutationResolver {

    public Boolean uploadFile(FileUpload fileUpload) {

        String fileContentType = fileUpload.getContentType();
        byte[] fileContent = fileUpload.getContent();

        // Do something in order to persist the file :)


        return true;
    }
}

在架構中,您將其聲明為:

scalar FileUpload

type Mutation {
    uploadFile(fileUpload: FileUpload): Boolean
}

如果它對您不起作用,請告訴我:)

只是為了添加上面的答案,對於像我這樣可以使用 GraphQLSchemaGenerator 與模式優先方法找到 0 個文件上傳示例的人,您只需創建一個 TypeMapper 並將其添加到您的 GraphQLSchemaGenerator 中:

public class FileUploadMapper implements TypeMapper {

  @Override
  public GraphQLOutputType toGraphQLType(
      final AnnotatedType javaType, final OperationMapper operationMapper,
      final Set<Class<? extends TypeMapper>> mappersToSkip, final BuildContext buildContext) {
    return MyScalars.FileUpload;
  }

  @Override
  public GraphQLInputType toGraphQLInputType(
      final AnnotatedType javaType, final OperationMapper operationMapper,
      final Set<Class<? extends TypeMapper>> mappersToSkip, final BuildContext buildContext) {
    return MyScalars.FileUpload;
  }

  @Override
  public boolean supports(final AnnotatedType type) {
     return type.getType().equals(FileUpload.class); //class of your fileUpload POJO from the previous answer
  }
}

然后在您構建 GraphQLSchema 的 GraphQL @Configuration 文件中:

public GraphQLSchema schema(GraphQLSchemaGenerator schemaGenerator) {
    return schemaGenerator
        .withTypeMappers(new FileUploadMapper()) //add this line
        .generate();
  }

然后在你的突變解析器中

  @GraphQLMutation(name = "fileUpload")
  public void fileUpload(      
      @GraphQLArgument(name = "file") FileUpload fileUpload //type here must be the POJO.class referenced in your TypeMapper
  ) {
    //do something with the byte[] from fileUpload.getContent();
    return;
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM