Comprende el código correctamente previamente a utilizarlo a tu proyecto y si tquieres aportar algo puedes dejarlo en la sección de comentarios.
Solución:
-
definir un tipo escalar en nuestro esquema
scalar Upload
y debemos configurar GraphQLScalarType para cargar, use esto a continuación:
@Configuration public class GraphqlConfig @Bean public GraphQLScalarType uploadScalarDefine() return ApolloScalars.Upload;
-
luego definiríamos una mutación en el esquema y un GraphQLMutationResolver para testMultiFilesUpload
type Mutation testMultiFilesUpload(files: [Upload!]!): Boolean
aquí está Resolver:
public Boolean testMultiFilesUpload(List parts, DataFetchingEnvironment env)
// get file parts from DataFetchingEnvironment, the parts parameter is not use
List 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;
}
-
configurar un deserializador jackson para
javax.servlet.http.Part
y registrarlo en ObjectMapperpublic class PartDeserializer extends JsonDeserializer
@Override public Part deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException return null; por qué regresamos null? porque el
List
siempre null , En el método del resolutor, obtenga el argumento de las partes del DataFetchingEnvironment;parts environment.getArgument (“archivos”)
regístrelo en 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;
- Para probar esto, publique los siguientes datos del formulario (usamos Postman) en el punto final de 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
como esto:
recuerde seleccionar la opción de datos de formulario
a través de esto podemos subir varios archivos
El principal problema es que graphql-java-tools
puede tener problemas para hacer el mapeo de campos para los resolutores que contienen campos de tipos no básicos como List
, String
, Integer
, Boolean
, etc …
Resolvimos este problema simplemente creando nuestro propio escalar personalizado que es básicamente como ApolloScalar.Upload
. Pero en lugar de devolver un objeto del tipo Part
, devolvemos nuestro propio tipo de resolución FileUpload
que contiene el contentType como String
y el inputStream como byte[]
, entonces el mapeo de campo funciona y podemos leer el byte[]
dentro del resolutor.
Primero, configure el nuevo tipo que se usará en el resolutor:
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;
Luego creamos un escalar personalizado que se parece mucho a ApolloScalars.Upload
, pero devuelve nuestro propio tipo de resolución FileUpload
:
public class MyScalars
public static final GraphQLScalarType FileUpload = new GraphQLScalarType(
"FileUpload",
"A file part in a multipart request",
new Coercing()
@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");
);
En el resolutor, ahora podrá obtener el archivo de los argumentos del resolutor:
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;
En el esquema, lo declaras como:
scalar FileUpload
type Mutation
uploadFile(fileUpload: FileUpload): Boolean
Avísame si no te funciona 🙂