Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File uploads #114

Closed
wpK opened this issue Jul 24, 2017 · 25 comments
Closed

File uploads #114

wpK opened this issue Jul 24, 2017 · 25 comments
Labels
enhancement Issues outlining new things we want to do or things that will make our lives as devs easier
Milestone

Comments

@wpK
Copy link

wpK commented Jul 24, 2017

I'm adding support for uploading files within the client. Is there any interest in a PR to add it into the project?

Since the GraphQL spec does not mention file uploads, I'm considering just adding another function to the client that accepts [String: GraphQLFile] with GraphQLFile having the mime type/file data.

Any other suggestions are welcome.

@martijnwalraven
Copy link
Contributor

Support for file uploads would be great, so definitely interested in a PR! We may want to coordinate with related efforts in the Apollo JavaScript client and server however, see here.

@jaydenseric
Copy link

jaydenseric commented Jul 25, 2017

I would suggest following the graphql-upload and apollo-upload-client strategy using multipart forms. This approach is making its way into the core.

You can see the structure for standard and batched multipart GraphQL requests here. A single GraphQL server will be able to handle uploads from all clients the same way if they follow this pattern.

Edit: The final GraphQL multipart request spec: https://github.com/jaydenseric/graphql-multipart-request-spec

@wpK
Copy link
Author

wpK commented Jul 25, 2017

@jaydenseric Thanks for the link. Given what you guys are working on now my implementation might be too basic. My goal was to add the functionality of adding files (provide perform() with a list of files) and allow the caller to update the mutation manually.

So given the GraphQL example you used here:

let imageData: Data = ...
let pdfData: Data = ...

// GraphQLFile would be a class in apollo-ios
let files = [GraphQLFile(fieldName: "files.0", originalName: "test-file-1.png", mimeType: "image/png", "data": imageData), GraphQLFile(fieldName: "files.1", originalName: "test-file-2.pdf", mimeType: "application/pdf", "data": pdfData)]

// Here we can format the upload however we want
let mutation = MultipleUploadMutation(files: files.map({Upload(name: $0.originalName, type: $0.mimeType, size: $0.count)}))

let client = ApolloClient( ... )
client.perform(mutation: mutation, files: files)

This allows the apollo-ios to support multipart uploads while also not forcing a specific spec.

The HTTPNetworkTransport class would check to see if there are any files (files is optional and defaults to nil). If there are files, then it would create a multipart request instead.

@dfmarulanda
Copy link

We still don't have support for multipart upload via apollo-ios?

@0xTomTom
Copy link

0xTomTom commented Jan 2, 2019

Would love to see this added

@designatednerd designatednerd added the enhancement Issues outlining new things we want to do or things that will make our lives as devs easier label Jun 25, 2019
@designatednerd
Copy link
Contributor

Please take a look at #626 if you're interested in this feature and let us know if there's anything there which is missing!

@designatednerd designatednerd added this to the 0.13.0 milestone Jul 22, 2019
@victormihaita
Copy link

@designatednerd In our API we are using the Upload type-alias as an attribute in the mutation. So for example when updating a profile, we can pass the image file in the mutation as one of the parameters so we can update the profile image and the rest of the info in one step. Unfortunately, on our iOS client we cannot use it because Apollo doesn't support this Upload type for now.
Is it possible to have also this functionality implemented?

@designatednerd
Copy link
Contributor

@victormihaita I'd certainly take a look at a PR implementing that addition, but as far as I can tell, it's not part of the final multi-part request spec, which is what we're adding support for with #626.

@victormihaita
Copy link

@designatednerd I know is not. Is just a topic related to this PR and I thought it would be the best place to mention it :)

@designatednerd
Copy link
Contributor

Fair enough - I think one thing to be aware of in terms of a PR is looking at how this functionality is handled on other clients - not just for iOS but other GraphQL clients, especially in the absence of a spec for stuff like this. We can discuss further on Spectrum if you'd like!

@designatednerd
Copy link
Contributor

Uploads conforming to the multi-part request spec have shipped with 0.13.0 - if you have further needs, please open a new issue or even better, open a PR 😄

@emmya
Copy link

emmya commented Oct 4, 2019

I know this is closed but +1... really need the Upload type to be supported in ios!

@kimdv
Copy link
Contributor

kimdv commented Oct 4, 2019

@emmya it should already be supported! 🚀🚀

https://www.apollographql.com/docs/ios/mutations/#uploading-files

@designatednerd
Copy link
Contributor

@emmya Yes ^ that is the solution!

@salman-ikonami
Copy link

@jaydenseric Thanks for the link. Given what you guys are working on now my implementation might be too basic. My goal was to add the functionality of adding files (provide perform() with a list of files) and allow the caller to update the mutation manually.

So given the GraphQL example you used here:

let imageData: Data = ...
let pdfData: Data = ...

// GraphQLFile would be a class in apollo-ios
let files = [GraphQLFile(fieldName: "files.0", originalName: "test-file-1.png", mimeType: "image/png", "data": imageData), GraphQLFile(fieldName: "files.1", originalName: "test-file-2.pdf", mimeType: "application/pdf", "data": pdfData)]

// Here we can format the upload however we want
let mutation = MultipleUploadMutation(files: files.map({Upload(name: $0.originalName, type: $0.mimeType, size: $0.count)}))

let client = ApolloClient( ... )
client.perform(mutation: mutation, files: files)

This allows the apollo-ios to support multipart uploads while also not forcing a specific spec.

The HTTPNetworkTransport class would check to see if there are any files (files is optional and defaults to nil). If there are files, then it would create a multipart request instead.

how to upload single file using iOS Client

@salman-ikonami
Copy link

mutation uploadFile($file:Upload!,$business_id:String!){
uploadFile(file: $file,business_id: $business_id)
}

this is my mutation how to call this mutation

@emmya
Copy link

emmya commented Nov 15, 2019

Hey guys,

Our server has a pretty basic upload file mutation that works great with our web client:

image

When my ios dev generates the apollo graphql API on his end using the server-side schema, it converts the Upload type to a String.

He then follows the documentation but what gets received by the server is.... a string.

I'll have him elaborate here but this is blocking us currently. Is tehre something I need to do special on the server-side to support ios? We are at a loss, any help is appreciated. Thank you!

@PatrickAdams
Copy link

In regards to what @emmya mentioned above. This is what I am doing on my end to perform the upload on iOS...

if let imageData = profileImage?.jpegData(compressionQuality: 0.7) {
    let uploadFile = GraphQLFile(fieldName: "profile_image", originalName: "profile_image", mimeType: "image/jpeg", data: imageData)
    let uploadPhotoMutation = UploadProfilePhotoMutation(input: SingleFileUploadInput(file: uploadFile))
    Apollo.shared.client.upload(operation: uploadPhotoMutation, files: [uploadFile]) { result in
    }
  }
}

@designatednerd
Copy link
Contributor

@PatrickAdams Do you have a version of SingleFileUploadInput which takes a GraphQL file?

@designatednerd
Copy link
Contributor

@emmya There's a bit of silliness that has to be done because of the way the combination of iOS codegen and the Upload spec works. The upload documentation has an example of what you need to do.

If that doesn't help, let me know.

@PatrickAdams
Copy link

Yes, I've look at the documentation many times. I see in the documentation they pass the string "a" into the UploadFileMutation but the actual file is attached under the files array parameter on the Apollo upload function. I've been wondering if its possible @emmya is just grabbing the wrong value on the backend, instead of the file, she's getting the string.

@designatednerd
Copy link
Contributor

There's some goofiness where the GraphQLFile's fieldName property has to precisely match the name of the field being sent to the server, or the uploader can't figure out how to replace the passed-in string with the appropriate data.

@DeekshaApptunix
Copy link

I need to upload image in following mutation

mutation updateUserProfile ($userId:ID!,$userImage:Upload,$userDetail:UserInput!){
updateUser(id:$userId,file:$userImage,user:$userDetail){
fname
lname
}
}

Can you please let me know how can I send a image data in $file

@EliBrahim
Copy link

EliBrahim commented Jul 5, 2022

Hi
I am having an issue with file upload on iOS
I set the Upload variable on my side, but when I send it to server, the server says string is not an Upload

Do I need any mapping of type  Upload or any custom type ?

// ...
		let fileURL = message.attachmentUrl?.asURL
		var files: [GraphQLFile] = []
			 
		if let attachmentUrl = message.attachmentUrl,
		let image = UIImage(contentsOfFile: attachmentUrl),
		let data = UIImageJPEGRepresentation(image, 1) {
		
		let file = GraphQLFile(
			fieldName: "attachment",
			originalName: fileURL?.lastPathComponent ?? "image.jpeg",
			mimeType: "image/jpeg",
			data: data
		)
		
		// attachment here is type Upload
		messageInput.attachment = "attachment"
		files.append(file)
	                
                // Actually upload the file
                let mutation = SendChatMessageMutation.init(message: messageInput)
                
                self.performUpload(mutation, files: files)
	
        }
	
	/// Send message and upload file
	func performUpload(_ mutation: SendChatMessageMutation, files: [GraphQLFile]) {
		self.network.apolloClient.upload(operation: mutation, files: files, queue: .global()) { result in
			
			switch result {
			case .success(let data):
         }
// ...

@calvincestari
Copy link
Member

Hi @EliBrahim, which server are you using and does it conform to the GraphQL multipart request spec?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Issues outlining new things we want to do or things that will make our lives as devs easier
Projects
None yet
Development

No branches or pull requests