jaydenseric / apollo-upload-client

A terminating Apollo Link for Apollo Client that fetches a GraphQL multipart request if the GraphQL variables contain files (by default FileList, File, or Blob instances), or else fetches a regular GraphQL POST or GET request (depending on the config and GraphQL operation).
https://npm.im/apollo-upload-client
1.53k stars 155 forks source link

Attach file info to file #52

Closed viiiprock closed 6 years ago

viiiprock commented 6 years ago

Hi @jaydenseric How I could attach file info together to the uploaded file such as image dimensions ?

jaydenseric commented 6 years ago

You have a few options. The naive approach would be to trust the client, and have mutation variables accompanying the one holding the file for width, height, etc. A better approach would be to inspect the actual file uploaded in the resolvers.

Depending what sort of file properties you are attempting to inspect, you might be able to read them passively from the stream or you may need to stream the file to a temporary location on the filesystem before looking at it. I don't have any code example handy, but you may have some luck with sharp metadata.

viiiprock commented 6 years ago

thanks @jaydenseric

viiiprock commented 6 years ago

Sorry to boring you again @jaydenseric I have an issue, but I don't understand where is it come from (front-end of api). So I post my snippets here again.

API

service

const imgDir = './public/uploads'
mkdirp.sync(imgDir)
const storeFS = ({ stream, filename }) => {
  const id = uuid.v4()
  const path = `${imgDir}/${id}-${filename}`
  return new Promise((resolve, reject) =>
    stream
      .on('error', error => {
        // Delete the truncated file
        (error || stream.truncated) && unlinkSync(path)
        reject(error)
      })
      .on('end', () => resolve({ id, path }))
      .pipe(createWriteStream(path))
  )
}

const processUpload = async upload => {
  const { stream, filename, mimetype, encoding } = await upload
  const { id, path } = await storeFS({ stream, filename })
  return { id, filename, mimetype, encoding, path }
}

resolver

export const uploadImage = async (root, { images }, { mongo: { Images }, user }) => {
  const { resolve, reject } = await all(images.map(processUpload))
  if (reject.length) {
    reject.forEach(({ name, message }) => {
      throw new Error(`${name}: ${message}`)
    })
  }
  console.log('RESULT', resolve)
}

Frontend - with create-react-app starter kit

class Uploader extends React.Component {
  handleChange = e => {
    e.preventDefault()
    const { mutate } = this.props
    e.target.validity.valid &&
    mutate({
      variables: { images: e.target.files }
    })
  }

  render() {
    return (
      <div>
        <input type="file" multiple onChange={this.handleChange}/>
      </div>
    )
  }
}

export default graphql(gql`
  mutation($images: [Upload!]!) {
    uploadImage(images: $images) {
      id
      filename
      encoding
      mimetype
      path
    }
  }
`)(Uploader)

The file upload is success but I got no info inside the file, would you mind to give me a hint to define where issue came from.

screen shot 2018-01-08 at 7 01 15 am

note Your example works well

jaydenseric commented 6 years ago

Did the original file have that metadata before it was uploaded?

viiiprock commented 6 years ago

yes @jaydenseric , your example works well. btw, i used Express, not Koa

giautm commented 6 years ago

@viiiprock : can you use shasum from the command line to checksum of both files? I tried to run apollo-upload-examples and metadata still existing after uploaded. PS: use mdls to show metadata of the file.

viiiprock commented 6 years ago

The example works fine, I've used FileReader() to read width, height info on client, so I guessed that it missed something somewhere on the server side, so I'm trying to step by step reproduce from the resolver :D mdls might help, thanks @giautm

viiiprock commented 6 years ago

@giautm This is really weird, I can't image what happened, if I copied the uploaded file to other location, the info existed again.

giautm commented 6 years ago

@viiiprock : Ya, the first time I upload files to the server side, metadata does not exist if I use Get File Info on the uploaded file. Maybe this issues related to Finder on Mac OS.

jaydenseric commented 6 years ago

See this Hacker News post and comment.

Apparently macOS puts "extended attributes" (xattrs) on files, and these can differ between OS versions. Apparently they often get lost when making certain types of transfers.

viiiprock commented 6 years ago

incredible, seems it make sense now. For who has same issue Just put repo at Users (desktop, document...) instead of Volumes directory.