Skip to content

Commit

Permalink
Uploading large attachments (fixes #999) (#1403)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbert authored Jun 21, 2018
1 parent a0ef9f8 commit e079772
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 69 deletions.
72 changes: 26 additions & 46 deletions src/app/resources/resources-add.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ValidatorService } from '../validators/validator.service';
import * as constants from './resources-constants';
import * as JSZip from 'jszip/dist/jszip.min';
import { Observable, of, forkJoin } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { PlanetMessageService } from '../shared/planet-message.service';
import { debug } from '../debug-operator';

Expand Down Expand Up @@ -101,46 +102,37 @@ export class ResourcesAddComponent implements OnInit {
return mediaTypes.find((type) => mimeType.indexOf(type) > -1) || 'other';
}

// Creates an observer which reads one file then outputs its data
private fileReaderObs(file, mediaType) {
const reader = new FileReader();
const obs = Observable.create((observer) => {
reader.onload = () => {
// FileReader result has file type at start of string, need to remove for CouchDB
const fileData = reader.result.split(',')[1],
attachments = {};
attachments[file.name] = {
content_type: file.type,
data: fileData
};
const resource = {
filename: file.name,
_attachments: attachments,
mediaType: mediaType
};
observer.next(resource);
observer.complete();
};
});
reader.readAsDataURL(file);
return obs;
private singleAttachment(file, mediaType) {
const resource = {
filename: file.name,
mediaType: mediaType,
_attachments: {}
};
return of({ resource, file });
}

onSubmit() {
if (this.resourceForm.valid) {
this.isSubmitted = true;
const fileObs: Observable<any> = this.createFileObs();
fileObs.pipe(debug('Preparing file for upload')).subscribe((resource) => {
fileObs.pipe(debug('Preparing file for upload')).subscribe(({ resource, file }) => {
const { _id, _rev } = this.existingResource;
// If we are removing the attachment, only keep id and rev from existing resource. Otherwise use all props
const existingData = this.deleteAttachment ? { _id, _rev } : this.existingResource;
// Start with empty object so this.resourceForm.value does not change
const newResource = Object.assign({}, existingData, this.resourceForm.value, resource);
if (this.route.snapshot.url[0].path === 'update') {
this.updateResource(newResource);
} else {
this.addResource(newResource);
}
const obs = this.pageType === 'Update' ? this.updateResource(newResource) : this.addResource(newResource);
const message = this.pageType === 'Update' ? 'Resource Updated Successfully' : 'New Resource Created';
obs.pipe(switchMap((res) => {
if (file) {
const opts = { headers: { 'Content-Type': file.type } };
return this.couchService.putAttachment(this.dbName + '/' + res.id + '/' + file.name + '?rev=' + res.rev, file, opts);
}
return of({});
})).subscribe(() => {
this.router.navigate([ '/resources' ]);
this.planetMessageService.showMessage(message);
}, (err) => this.planetMessageService.showAlert('There was an error with this resource'));
});
} else {
Object.keys(this.resourceForm.controls).forEach(field => {
Expand All @@ -156,33 +148,21 @@ export class ResourcesAddComponent implements OnInit {
switch (mediaType) {
case undefined:
// Creates an observable that immediately returns an empty object
return of({});
return of({ resource: {} });
case 'zip':
return this.zipObs(this.file);
default:
return this.fileReaderObs(this.file, mediaType);
return this.singleAttachment(this.file, mediaType);
}
}

addResource(resourceInfo) {
// ...is the rest syntax for object destructuring
this.couchService.post(this.dbName, { ...resourceInfo }).subscribe(() => {
this.router.navigate([ '/resources' ]);
this.planetMessageService.showMessage('New Resource Created');
}, (err) => {
// Connect to an error display component to show user that an error has occurred
console.log(err);
});
return this.couchService.post(this.dbName, { ...resourceInfo });
}

updateResource(resourceInfo) {
this.couchService.put(this.dbName + '/' + resourceInfo._id, { ...resourceInfo }).subscribe(() => {
this.router.navigate([ '/resources' ]);
this.planetMessageService.showMessage('Resource Updated Successfully');
}, (err) => {
// Connect to an error display component to show user that an error has occurred
console.log(err);
});
return this.couchService.put(this.dbName + '/' + resourceInfo._id, { ...resourceInfo });
}

deleteAttachmentToggle(event) {
Expand Down Expand Up @@ -235,7 +215,7 @@ export class ResourcesAddComponent implements OnInit {
return newFilesObj;
}, {});
// Leave filename blank (since it is many files) and call mediaType 'HTML'
observer.next({ filename: '', mediaType: 'HTML', _attachments: filesObj });
observer.next({ resource: { filename: '', mediaType: 'HTML', _attachments: filesObj } });
observer.complete();
}, (error) => {
console.log(error);
Expand Down
27 changes: 4 additions & 23 deletions src/app/shared/couchdb.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export class CouchService {
return this.couchDBReq('delete', db, this.setOpts(opts));
}

putAttachment(db: string, file: FormData, opts?: any) {
return this.couchDBReq('put', db, this.setOpts(opts), file);
}

allDocs(db: string, opts?: any) {
return this.couchDBReq('get', db + '/_all_docs?include_docs=true', this.setOpts(opts)).pipe(map((data: any) => {
// _all_docs returns object with rows array of objects with 'doc' property that has an object with the data.
Expand All @@ -80,27 +84,4 @@ export class CouchService {
return this.http.request(req);
}

// Reads a file as a Base64 string to append to object sent to CouchDB
prepAttachment(file) {
const reader = new FileReader();
const obs = Observable.create((observer) => {
reader.onload = () => {
// FileReader result has file type at start of string, need to remove for CouchDB
const fileData = reader.result.split(',')[1],
attachments = {};
attachments[file.name] = {
content_type: file.type,
data: fileData
};
const attachmentObject = {
_attachments: attachments
};
observer.next(attachmentObject);
observer.complete();
};
});
reader.readAsDataURL(file);
return obs;
}

}

0 comments on commit e079772

Please sign in to comment.