Open sandervanhooft opened 7 years ago
Hmm ... I don't exactly understand your problem. In your case, files[0].path
points to the uploaded file, so you can move it to your uploads folder or do whatever with it.
When I find some time, I will make an example app with client & server demonstrating file uploads. Until then ... this is how I handle files on the server:
schema
addSalonProfilePicture(id: Int!, files: [UploadedFile!]!): SalonPicture
mutation
async addSalonProfilePicture(root, { id, files }, context) {
// must be logged in
if (!context.user) throw new Error('Must be logged in!')
// load salon
const salon = await Salon.find({ where: { id } })
if (!salon) throw new Error('Salon not found!')
// upload picture
const path = await uploadPicture(`salon/${salon.slug}/profile`, files[0])
// parse uploaded path
const info = parse(path)
// build picture data
const data = {
salon_id: salon.id,
type: 'profile',
filename: `${info.base}`,
active: true,
created_on: new Date(),
created_by: context.user.id,
}
// insert into database
const picture = await SalonPicture.create(data)
// return picture
return picture
}
upload
export async function uploadPicture(folder, file, name) {
if (!file.path || !file.originalname || !file.mimetype) {
throw new Error('Invalid file parameters!')
}
if (config.picture.allowedMimeTypes.indexOf(file.mimetype) === -1) {
throw new Error('Invalid picture type!')
}
const base = `${config.documents.path}/${folder}`
const finfo = parse(file.originalname)
const filename = createSlug(name || finfo.name).toLowerCase()
const path = await generateUniquePath(`${base}/${filename}${finfo.ext}`)
await move(file.path, path)
const info = parse(path)
for (let size in config.picture.sizes) {
let { width, height, crop, quality } = config.picture.sizes[size]
let destination = `${base}/${info.name}-${size}${info.ext}`
if (crop) {
await cropImage(path, destination, width, height, quality)
} else {
await resizeImage(path, destination, width, height, quality)
}
}
return path
}
@HriBB
Thanks for the quick response, seems like I have to handle file.path as your code suggests.
I'll give this a try tomorrow and let you know! :)
By the way, what is that parse(...)
function, where is it coming from? Are you using a helper library?
import { parse } from 'path'
We should create an example app to demonstrate the entire upload flow. Maybe we can use https://github.com/apollostack/react-apollo/tree/master/examples/create-react-app
You can use MemoryStore for Multer to avoid having to read the file after the fact, as each file object will then contain a Buffer object with the uploaded file contents
@thebigredgeek Do you have example code for that?
You can use MemoryStore for Multer to avoid having to read the file after the fact, as each file object will then contain a Buffer object with the uploaded file contents
Just look at multer docs. It's in the readme. This package doesn't care about how the file is shaped
@thebigredgeek I just did, learned a lot from this issue (parse, multer, fs-extra)....
After a lot of trial and error, I decided to go without the memoryStore/buffer.
In order to pass the file on to the REST endpoint, I had to:
Resolver method:
uploadUserAvatar(root: Function, args: Object) {
const tmpFile = args.files[0]
const tmpFileParsed = parse(tmpFile.path)
const tmpAvatarDirPath = `${tmpFileParsed.dir}/avatar-${tmpFileParsed.base}`
const newTmpFilePath = `${tmpAvatarDirPath}/${tmpFile.originalname}`
fs.move(tmpFile.path, newTmpFilePath, function (err) {
if (err) return console.error(err)
return Viewer.uploadAvatar(args.token, newTmpFilePath).then(
// cleanup tmpAvatarDir
fs.remove(tmpAvatarDirPath, function (err) {
if (err) return console.error(err)
})
)
})
}
Connector method:
uploadAvatar(token: string, avatarPath: string){
const options = {
method: 'POST',
uri: this.buildRestURL('settings/profile/uploadavatar'),
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
formData: {
avatar: fs.createReadStream(avatarPath),
},
json: true,
}
return rp(options)
.then((res) => {
return {
...res.data,
token
}
})
.catch((err) => console.error("\nViewer.uploadAvatar ERROR:", err.message))
}
}
@HriBB Do you perhaps have an example how to handle the file on the graphql server?
If I forward it to my REST endpoint, it doesn't seem to be recognised as a file.
My resolver:
Viewer connection:
Console.log(args.files) from resolver: