Skip to content

Commit ab07e4f

Browse files
committed
Add delete_by_token_sample
1 parent ff9c4e7 commit ab07e4f

File tree

3 files changed

+128
-104
lines changed

3 files changed

+128
-104
lines changed

samples/photo_album/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This sample project shows:
66
1. How to use the Cloudinary Angular directives.
77
2. How to upload files to Cloudinary in an unsigned manner, using an upload preset. The upload control is based on the open source file uploader [ng2-file-upload](https://github.com/valor-software/ng2-file-upload)
88
3. How to use the dynamic list resource in order to maintain a short list of resources aggregated by tags.
9+
4. How to delete an image uploaded from the browser with an unsigned upload. You can find additional details in this [knowledge base article](https://support.cloudinary.com/hc/en-us/articles/202521132-How-to-delete-an-image-from-the-client-side-). Don't forget to set the `Return delete token` setting of your unsigned upload preset to `true`.
910

1011
## Configuration ##
1112

samples/photo_album/app/js/photo-album/photo-upload.component.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ <h2>Direct upload from the browser with Angular File Upload</h2>
2424
</div>
2525
</form>
2626
<h2>Status</h2>
27-
<div class="file" *ngFor="let response of responses">
27+
<div class="file" *ngFor="let response of responses; let i = index">
2828
<h3>{{response.file.name}}</h3>
29+
<button class="delete-image" *ngIf="!!response.data.delete_token" (click)="deleteImage(response.data, i)">Delete image</button>
2930
<div class="status">
3031
Uploading... {{response.progress}}%
3132
<div *ngIf="!response.status">In progress</div>
@@ -53,4 +54,4 @@ <h3>{{response.file.name}}</h3>
5354

5455
</div>
5556

56-
<a routerLink="" class="back_link">Back to list</a>
57+
<a routerLink="" class="back_link">Back to list</a>
Lines changed: 124 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,138 @@
1-
import {Component, OnInit, Input, NgZone} from '@angular/core';
2-
import {FileUploader, FileUploaderOptions, ParsedResponseHeaders} from 'ng2-file-upload';
3-
import {Cloudinary} from '@cloudinary/angular';
1+
import { Component, OnInit, Input, NgZone } from '@angular/core';
2+
import { Http, Response, RequestOptions, Headers } from '@angular/http';
3+
import { FileUploader, FileUploaderOptions, ParsedResponseHeaders } from 'ng2-file-upload';
4+
import 'rxjs/add/operator/toPromise';
5+
import { Cloudinary } from '@cloudinary/angular';
46

57
@Component({
6-
selector: 'photo-list',
7-
templateUrl: 'photo-upload.component.html'
8+
selector: 'photo-list',
9+
templateUrl: 'photo-upload.component.html'
810
})
911
export class PhotoUploadComponent implements OnInit {
1012

11-
@Input()
12-
responses: Array<any>;
13+
@Input()
14+
responses: Array<any>;
1315

14-
private hasBaseDropZoneOver: boolean = false;
15-
private uploader: FileUploader;
16-
private title: string;
16+
private hasBaseDropZoneOver: boolean = false;
17+
private uploader: FileUploader;
18+
private title: string;
1719

18-
constructor(
19-
private cloudinary: Cloudinary,
20-
private zone: NgZone
21-
) {
22-
this.responses = [];
23-
this.title = '';
24-
}
20+
constructor(
21+
private cloudinary: Cloudinary,
22+
private zone: NgZone,
23+
private http: Http
24+
) {
25+
this.responses = [];
26+
this.title = '';
27+
}
2528

26-
ngOnInit(): void {
27-
const uploaderOptions: FileUploaderOptions = {
28-
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().cloud_name}/upload`,
29-
autoUpload: true,
30-
isHTML5: true,
31-
removeAfterUpload: true,
32-
headers: [
33-
{
34-
name: 'X-Requested-With',
35-
value: 'XMLHttpRequest'
36-
}
37-
]
38-
};
39-
this.uploader = new FileUploader(uploaderOptions);
40-
41-
// Add custom tag for displaying the uploaded photo in the list
42-
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
43-
form.append('upload_preset', this.cloudinary.config().upload_preset);
44-
let tags = 'myphotoalbum';
45-
if (this.title) {
46-
form.append('context', `photo=${this.title}`);
47-
tags = `myphotoalbum,${this.title}`;
48-
}
49-
form.append('tags', tags);
50-
form.append('file', fileItem);
51-
52-
fileItem.withCredentials = false;
53-
return { fileItem, form };
54-
};
55-
56-
// Insert or update an entry in the responses array
57-
const upsertResponse = fileItem => {
58-
59-
// Run the update in a custom zone since for some reason change detection isn't performed
60-
// as part of the XHR request to upload the files.
61-
// Running in a custom zone forces change detection
62-
this.zone.run(() => {
63-
// Update an existing entry if it's upload hasn't completed yet
64-
65-
// Find the id of an existing item
66-
const existingId = this.responses.reduce((prev, current, index) => {
67-
if (current.file.name === fileItem.file.name && !current.status) {
68-
return index;
69-
}
70-
return prev;
71-
}, -1);
72-
if (existingId > -1) {
73-
// Update existing item with new data
74-
this.responses[existingId] = Object.assign(this.responses[existingId], fileItem);
75-
} else {
76-
// Create new response
77-
this.responses.push(fileItem);
78-
}
79-
});
80-
};
81-
82-
this.uploader.onCompleteItem = (item: any, response: string, status: number, headers: ParsedResponseHeaders) =>
83-
upsertResponse(
84-
{
85-
file: item.file,
86-
status,
87-
data: JSON.parse(response)
88-
}
89-
);
90-
91-
this.uploader.onProgressItem = (fileItem: any, progress: any) =>
92-
upsertResponse(
93-
{
94-
file: fileItem.file,
95-
progress
96-
}
97-
);
98-
}
29+
ngOnInit(): void {
30+
const uploaderOptions: FileUploaderOptions = {
31+
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().cloud_name}/upload`,
32+
autoUpload: true,
33+
isHTML5: true,
34+
removeAfterUpload: true,
35+
headers: [
36+
{
37+
name: 'X-Requested-With',
38+
value: 'XMLHttpRequest'
39+
}
40+
]
41+
};
42+
this.uploader = new FileUploader(uploaderOptions);
9943

100-
updateTitle(value: string) {
101-
this.title = value;
102-
}
44+
// Add custom tag for displaying the uploaded photo in the list
45+
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
46+
form.append('upload_preset', this.cloudinary.config().upload_preset);
47+
let tags = 'myphotoalbum';
48+
if (this.title) {
49+
form.append('context', `photo=${this.title}`);
50+
tags = `myphotoalbum,${this.title}`;
51+
}
52+
form.append('tags', tags);
53+
form.append('file', fileItem);
10354

104-
public fileOverBase(e: any): void {
105-
this.hasBaseDropZoneOver = e;
106-
}
55+
fileItem.withCredentials = false;
56+
return { fileItem, form };
57+
};
58+
59+
// Insert or update an entry in the responses array
60+
const upsertResponse = fileItem => {
10761

108-
getFileProperties(fileProperties: any) {
109-
// Transforms Javascript Object to an iterable to be used by *ngFor
110-
if (!fileProperties) {
111-
return null;
62+
// Run the update in a custom zone since for some reason change detection isn't performed
63+
// as part of the XHR request to upload the files.
64+
// Running in a custom zone forces change detection
65+
this.zone.run(() => {
66+
// Update an existing entry if it's upload hasn't completed yet
67+
68+
// Find the id of an existing item
69+
const existingId = this.responses.reduce((prev, current, index) => {
70+
if (current.file.name === fileItem.file.name && !current.status) {
71+
return index;
72+
}
73+
return prev;
74+
}, -1);
75+
if (existingId > -1) {
76+
// Update existing item with new data
77+
this.responses[existingId] = Object.assign(this.responses[existingId], fileItem);
78+
} else {
79+
// Create new response
80+
this.responses.push(fileItem);
11281
}
113-
return Object.keys(fileProperties)
114-
.map((key) => ({ 'key': key, 'value': fileProperties[key] }));
82+
});
83+
};
84+
85+
this.uploader.onCompleteItem = (item: any, response: string, status: number, headers: ParsedResponseHeaders) =>
86+
upsertResponse(
87+
{
88+
file: item.file,
89+
status,
90+
data: JSON.parse(response)
91+
}
92+
);
93+
94+
this.uploader.onProgressItem = (fileItem: any, progress: any) =>
95+
upsertResponse(
96+
{
97+
file: fileItem.file,
98+
progress,
99+
data: {}
100+
}
101+
);
102+
}
103+
104+
updateTitle(value: string) {
105+
this.title = value;
106+
}
107+
108+
deleteImage = function (data: any, index: number) {
109+
const url = `https://api.cloudinary.com/v1_1/${this.cloudinary.config().cloud_name}/delete_by_token`;
110+
let headers = new Headers({ 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' });
111+
let options = new RequestOptions({ headers: headers });
112+
const body = {
113+
token: data.delete_token
114+
};
115+
this.http.post(url, body, options)
116+
.toPromise()
117+
.then((response) => {
118+
console.log(`Deleted image - ${data.public_id} ${response.json().result}`);
119+
// Remove deleted item for responses
120+
this.responses.splice(index, 1);
121+
}).catch((err: any) => {
122+
console.log(`Failed to delete image ${data.public_id} ${err}`);
123+
});
124+
};
125+
126+
fileOverBase(e: any): void {
127+
this.hasBaseDropZoneOver = e;
128+
}
129+
130+
getFileProperties(fileProperties: any) {
131+
// Transforms Javascript Object to an iterable to be used by *ngFor
132+
if (!fileProperties) {
133+
return null;
115134
}
135+
return Object.keys(fileProperties)
136+
.map((key) => ({ 'key': key, 'value': fileProperties[key] }));
137+
}
116138
}

0 commit comments

Comments
 (0)