Skip to content

Instantly share code, notes, and snippets.

@stalniy
Created November 1, 2016 10:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save stalniy/5bc1d59bdb69a605ff6ebef036729abb to your computer and use it in GitHub Desktop.
Save stalniy/5bc1d59bdb69a605ff6ebef036729abb to your computer and use it in GitHub Desktop.
Ionic/Cordova file upload
import { Inject } from 'lib/angular2'
import { File, Transfer } from 'ionic-native'
import { Events } from 'events'
import { Config } from 'config/env'
@Inject(Events, Config)
export class CordovaUploadTransport {
get cacheDirectory() {
return cordova.file.tempDirectory || cordova.file.cacheDirectory
}
constructor(events, config) {
this.events = events
this.defaultUrl = `${config.get('API_URL')}/files`
}
upload(file, options) {
return this._upload(file, options)
.then(uploadResult => JSON.parse(uploadResult.response))
.catch(uploadError => {
const response = this.parseError(uploadError)
this.events.publish('data:request:error', response)
return Promise.reject(response)
})
}
_upload(file, options) {
return this.createTmpFileFrom(file).then(fileEntry => {
const params = Object.assign({ headers: {}, chunkedMode: true }, options)
if (params.url) {
delete params.url
}
this.events.publish('data:request', params)
return this._uploadAndRemoveFile(fileEntry, options.url || this.defaultUrl, params)
}).catch(err => {
console.log(err)
return Promise.reject(err)
})
}
_uploadAndRemoveFile(fileEntry, url, params) {
const uploadRequest = new Transfer().upload(fileEntry.toURL(), url, params)
const removeFile = File.remove.bind(null, fileEntry)
uploadRequest.then(removeFile, removeFile)
return uploadRequest
}
createTmpFileFrom(file) {
// TODO: use File.writeFile when passing options to `File.getFile` is fixed in ionic-native
return File.resolveDirectoryUrl(this.cacheDirectory)
.then(fse => File.getFile(fse, `${Date.now()}.${file.name}`, { create: true }))
.then(fe => this.writeFile(fe, file))
}
writeFile(fe, file) {
return File.createWriter(fe)
.then(writer => File.write(writer, file))
.then(() => fe)
}
parseError(response) {
const uploadResponse = JSON.parse(response.body)
return {
status: uploadResponse.statusCode,
message: uploadResponse.message,
}
}
}
<ion-card>
<label class="file-upload">
<ion-spinner name="ios-small" [hidden]="!isUploading"></ion-spinner>
<ion-icon name="add" [hidden]="isUploading"> Add</ion-icon>
<input type="file" (change)="uploadFiles($event.target.files)" #fileInput [attr.accept]="accept" />
</label>
</ion-card>
import { Component, Input, Output, Renderer, ViewChild, EventEmitter } from '@angular/core'
import { Inject } from 'lib/angular2'
import template from './file-upload.html'
import './file-upload.scss'
export class FileUploadTransport {
upload() {
throw new Error('Upload transport is not specified.')
}
}
@Inject(FileUploadTransport, Renderer)
@Component({
template,
selector: 'file-upload',
exportAs: 'uploader',
})
export class FileUpload {
isMultiple = false
@Input()
set multiple(value) {
this.isMultiple = value === '' || Boolean(value)
}
@Input() accept = '*/*'
@Output() uploaded = new EventEmitter()
@Output() uploadError = new EventEmitter()
@ViewChild('fileInput') fileInput = null
constructor(transport, renderer, zone) {
this.transport = transport
this.renderer = renderer
this.zone = zone
}
ngAfterViewInit() {
if (this.isMultiple) {
this.renderer.setElementAttribute(this.fileInput.nativeElement, 'multiple', 'multiple')
}
}
uploadFiles(filesList) {
const files = Array.prototype.slice.call(filesList, 0)
const requests = files.map(this.uploadFile, this)
const finishUploading = () => this.isUploading = false
this.isUploading = true
return Promise.all(requests)
.then(finishUploading, finishUploading)
}
uploadFile(file) {
return this.transport.upload(file, this.fileToOptions(file))
.then(remoteFile => this.uploaded.emit(remoteFile))
.catch(e => this.uploadError.emit(e))
}
fileToOptions(file) {
return {
fileKey: 'file',
fileName: file.name,
mimeType: file.type,
}
}
}
@import 'variables';
file-upload {
width: 68px;
height: 68px;
margin: 5px;
ion-card {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
border-style: dashed;
border-width: 2px;
}
.file-upload {
position: relative;
display: flex;
align-items: center;
height: 100%;
width: 100%;
input {
position: absolute;
top: -1000px;
left: -1000px;
opacity: 0;
}
ion-icon, ion-spinner {
margin: 0 auto;
font-family: inherit;
}
}
&:hover, &:active {
ion-card {
border-color: color($colors, primary);
color: color($colors, primary);
}
}
&:active {
ion-icon {
margin-top: 5px;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment