Angular: 阴天错误 { "message": "cloud_name is disabled" }
Angular: Cloudinary error { "message": "cloud_name is disabled" }
我正在尝试通过 Angular 使用 Cloudinary 图像上传。
我已经按照他们的文档安装了图像上传器 sdk。我已经关注了组件和 html.
的一些示例项目
我的项目中一切正常,但是当我尝试上传图片时,我不断收到错误消息:
Upload completed with status code 401
error { "message": "cloud_name is disabled" }
这是我的 component.ts
:
import { Component, OnInit, Input, NgZone } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FileUploader, FileUploaderOptions, ParsedResponseHeaders, FileUploadModule } from 'ng2-file-upload';
import { Cloudinary } from '@cloudinary/angular-5.x';
// https://www.youtube.com/watch?v=YkvqLNcJz3Y
@Component({
selector: 'app-cloudinary',
templateUrl: './cloudinary.component.html',
styleUrls: ['./cloudinary.component.scss']
})
export class CloudinaryComponent implements OnInit {
@Input()
responses: Array<any>;
public hasBaseDropZoneOver: boolean = false;
public uploader: FileUploader;
private title: string;
constructor(
private cloudinary: Cloudinary,
private zone: NgZone,
private http: HttpClient
) {
this.responses = [];
this.title = '';
}
ngOnInit(): void {
// Create the file uploader, wire it to upload to your account
const uploaderOptions: FileUploaderOptions = {
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload`,
// Upload files automatically upon addition to upload queue
autoUpload: true,
// Use xhrTransport in favor of iframeTransport
isHTML5: true,
// Calculate progress independently for each uploaded file
removeAfterUpload: true,
// XHR request headers
headers: [
{
name: 'X-Requested-With',
value: 'XMLHttpRequest'
}
]
};
this.uploader = new FileUploader(uploaderOptions);
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
// Add Cloudinary's unsigned upload preset to the upload form
form.append('upload_preset', this.cloudinary.config().upload_preset);
// Add built-in and custom tags for displaying the uploaded photo in the list
let tags = 'myphotoalbum';
if (this.title) {
form.append('context', `photo=${this.title}`);
tags = `myphotoalbum,${this.title}`;
}
// Upload to a custom folder
// Note that by default, when uploading via the API, folders are not automatically created in your Media Library.
// In order to automatically create the folders based on the API requests,
// please go to your account upload settings and set the 'Auto-create folders' option to enabled.
form.append('folder', 'angular_sample');
// Add custom tags
form.append('tags', tags);
// Add file to upload
form.append('file', fileItem);
// Use default "withCredentials" value for CORS requests
fileItem.withCredentials = false;
return { fileItem, form };
};
// Insert or update an entry in the responses array
const upsertResponse = fileItem => {
// Run the update in a custom zone since for some reason change detection isn't performed
// as part of the XHR request to upload the files.
// Running in a custom zone forces change detection
this.zone.run(() => {
// Update an existing entry if it's upload hasn't completed yet
// Find the id of an existing item
const existingId = this.responses.reduce((prev, current, index) => {
if (current.file.name === fileItem.file.name && !current.status) {
return index;
}
return prev;
}, -1);
if (existingId > -1) {
// Update existing item with new data
this.responses[existingId] = Object.assign(this.responses[existingId], fileItem);
} else {
// Create new response
this.responses.push(fileItem);
}
});
};
// Update model on completion of uploading a file
this.uploader.onCompleteItem = (item: any, response: string, status: number, headers: ParsedResponseHeaders) =>
upsertResponse(
{
file: item.file,
status,
data: JSON.parse(response)
}
);
// Update model on upload progress event
this.uploader.onProgressItem = (fileItem: any, progress: any) =>
upsertResponse(
{
file: fileItem.file,
progress,
data: {}
}
);
}
updateTitle(value: string) {
this.title = value;
}
// Delete an uploaded image
// Requires setting "Return delete token" to "Yes" in your upload preset configuration
// See also https://support.cloudinary.com/hc/en-us/articles/202521132-How-to-delete-an-image-from-the-client-side-
deleteImage = function (data: any, index: number) {
const url = `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/delete_by_token`;
const headers = new Headers({ 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' });
const options = { headers: headers };
const body = {
token: data.delete_token
};
this.http.post(url, body, options).subscribe(response => {
console.log(`Deleted image - ${data.public_id} ${response.result}`);
// Remove deleted item for responses
this.responses.splice(index, 1);
});
};
fileOverBase(e: any): void {
this.hasBaseDropZoneOver = e;
}
getFileProperties(fileProperties: any) {
// Transforms Javascript Object to an iterable to be used by *ngFor
if (!fileProperties) {
return null;
}
return Object.keys(fileProperties)
.map((key) => ({ 'key': key, 'value': fileProperties[key] }));
}
}
这是我的 component.html
:
<h1>Image Upload to Cloudinary TESTING</h1>
<div id="direct_upload" ng2FileDrop [uploader]="uploader" (fileOver)="fileOverBase($event)" [ngClass]="{'nv-file-over': hasBaseDropZoneOver}">
<h1>New Photo</h1>
<h2>Direct upload from the browser with Angular File Upload</h2>
<p>You can also drag and drop an image file into the dashed area.</p>
<form>
<div class="form_line">
<label path="title">Title:</label>
<div class="form_controls">
<input type="text" class="form-control" #title placeholder="Title" (keyup.enter)="updateTitle(title.value)" (blur)="updateTitle(title.value)"
/>
</div>
</div>
<div class="form_line">
<label>Image:</label>
<div class="form_controls">
<div class="upload_button_holder">
<label class="upload_button" for="fileupload">Upload</label>
<!-- onChange hanlder resets the input value to get the change event when uploading the same file consecutively -->
<input type="file" id="fileupload" #fileInput ng2FileSelect [style.display]="'none'" [uploader]="uploader" (change)="fileInput.value=''"
multiple />
</div>
</div>
</div>
</form>
<h2>Status</h2>
<div class="file" *ngFor="let response of responses; let i = index">
<h3>{{response.file.name}}</h3>
<button class="delete-image" *ngIf="!!response.data.delete_token" (click)="deleteImage(response.data, i)">Delete image</button>
<div class="status">
Uploading... {{response.progress}}%
<div *ngIf="!response.status">In progress</div>
<div class="status-code" *ngIf="response.status">Upload completed with status code {{response.status}}</div>
</div>
<div class="progress-bar">
<div class="progress" role="progressbar" [style.width.%]="response.progress"></div>
</div>
<div class="form_line">
<div class="form_controls">
<div class="preview">
<!-- Consider using https://github.com/valor-software/ng2-file-upload/issues/461 for image preview -->
</div>
</div>
</div>
<div class="info">
<table>
<tr *ngFor="let property of getFileProperties(response.data)">
<td> {{ property.key }} </td>
<td> {{ property.value | json}} </td>
</tr>
</table>
</div>
</div>
</div>
在我的 app.module.ts
中,我进行了导入:
...
import { CloudinaryModule, CloudinaryConfiguration } from '@cloudinary/angular-5.x';
import { Cloudinary } from 'cloudinary-core';
import { FileUploadModule } from "ng2-file-upload";
@NgModule({
declarations: [
...
CloudinaryComponent
],
imports: [
...
// Cloudinary import
CloudinaryModule.forRoot({Cloudinary}, { cloud_name: 'CLOUDN_NAME' } as CloudinaryConfiguration),
FileUploadModule
],
providers: [
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
所以我真的不明白为什么它没有获得授权,但同样困扰我的是,在 Cloudinary 文档中 Angular 我不知道我应该把我的 API Key
和我的 API Secret
.
在我的项目中没有任何地方有像 systemjs.config.js
rollup-config.js
.
这样的文件
如果有人能帮助我,那就太棒了!
谢谢
在您的示例中,您需要更改云 URL
。
您有 https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload
,但您需要 https://api.cloudinary.com/v1_1/CLOUD_NAME/upload
。
你的https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/delete_by_token
也需要改成https://api.cloudinary.com/v1_1/CLOUD_NAME/delete_by_token
而您的 upload_preset
您需要将其从 form.append('upload_preset', this.cloudinary.config().upload_preset)
更改为 form.append('upload_preset', 'PRESET_NAME')
所以你的组件应该是这样的:
...
ngOnInit(): void {
// Create the file uploader, wire it to upload to your account
const uploaderOptions: FileUploaderOptions = {
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload`,
// Upload files automatically upon addition to upload queue
autoUpload: true,
// Use xhrTransport in favor of iframeTransport
isHTML5: true,
// Calculate progress independently for each uploaded file
removeAfterUpload: true,
// XHR request headers
headers: [
{
name: 'X-Requested-With',
value: 'XMLHttpRequest'
}
]
};
this.uploader = new FileUploader(uploaderOptions);
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
// Add Cloudinary's unsigned upload preset to the upload form
form.append('upload_preset', 'PRESET_NAME');
// Add built-in and custom tags for displaying the uploaded photo in the list
let tags = 'myphotoalbum';
if (this.title) {
form.append('context', `photo=${this.title}`);
tags = `myphotoalbum,${this.title}`;
}
// Upload to a custom folder
// Note that by default, when uploading via the API, folders are not automatically created in your Media Library.
// In order to automatically create the folders based on the API requests,
// please go to your account upload settings and set the 'Auto-create folders' option to enabled.
form.append('folder', 'angular_sample');
// Add custom tags
form.append('tags', tags);
// Add file to upload
form.append('file', fileItem);
// Use default "withCredentials" value for CORS requests
fileItem.withCredentials = false;
return { fileItem, form };
};
...
// Delete an uploaded image
// Requires setting "Return delete token" to "Yes" in your upload preset configuration
// See also https://support.cloudinary.com/hc/en-us/articles/202521132-How-to-delete-an-image-from-the-client-side-
deleteImage = function (data: any, index: number) {
const url = `https://api.cloudinary.com/v1_1/CLOUD_NAME/delete_by_token`;
const headers = new Headers({ 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' });
const options = { headers: headers };
const body = {
token: data.delete_token
};
...
对于 API key
,如果您要上传到已签名的 upload_preset
,您可以像这样在您的组件中添加密钥,如 sample project angular:
...
ngOnInit(): void {
console.log("initialized");
(window as any).cloudinary.createMediaLibrary(
{
cloud_name: "<cloud name>",
api_key: "<api key>",
button_class: "myBtn",
username: "<user email>",
button_caption: "Select Image or Video"
},
...
我正在尝试通过 Angular 使用 Cloudinary 图像上传。 我已经按照他们的文档安装了图像上传器 sdk。我已经关注了组件和 html.
的一些示例项目我的项目中一切正常,但是当我尝试上传图片时,我不断收到错误消息:
Upload completed with status code 401
error { "message": "cloud_name is disabled" }
这是我的 component.ts
:
import { Component, OnInit, Input, NgZone } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FileUploader, FileUploaderOptions, ParsedResponseHeaders, FileUploadModule } from 'ng2-file-upload';
import { Cloudinary } from '@cloudinary/angular-5.x';
// https://www.youtube.com/watch?v=YkvqLNcJz3Y
@Component({
selector: 'app-cloudinary',
templateUrl: './cloudinary.component.html',
styleUrls: ['./cloudinary.component.scss']
})
export class CloudinaryComponent implements OnInit {
@Input()
responses: Array<any>;
public hasBaseDropZoneOver: boolean = false;
public uploader: FileUploader;
private title: string;
constructor(
private cloudinary: Cloudinary,
private zone: NgZone,
private http: HttpClient
) {
this.responses = [];
this.title = '';
}
ngOnInit(): void {
// Create the file uploader, wire it to upload to your account
const uploaderOptions: FileUploaderOptions = {
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload`,
// Upload files automatically upon addition to upload queue
autoUpload: true,
// Use xhrTransport in favor of iframeTransport
isHTML5: true,
// Calculate progress independently for each uploaded file
removeAfterUpload: true,
// XHR request headers
headers: [
{
name: 'X-Requested-With',
value: 'XMLHttpRequest'
}
]
};
this.uploader = new FileUploader(uploaderOptions);
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
// Add Cloudinary's unsigned upload preset to the upload form
form.append('upload_preset', this.cloudinary.config().upload_preset);
// Add built-in and custom tags for displaying the uploaded photo in the list
let tags = 'myphotoalbum';
if (this.title) {
form.append('context', `photo=${this.title}`);
tags = `myphotoalbum,${this.title}`;
}
// Upload to a custom folder
// Note that by default, when uploading via the API, folders are not automatically created in your Media Library.
// In order to automatically create the folders based on the API requests,
// please go to your account upload settings and set the 'Auto-create folders' option to enabled.
form.append('folder', 'angular_sample');
// Add custom tags
form.append('tags', tags);
// Add file to upload
form.append('file', fileItem);
// Use default "withCredentials" value for CORS requests
fileItem.withCredentials = false;
return { fileItem, form };
};
// Insert or update an entry in the responses array
const upsertResponse = fileItem => {
// Run the update in a custom zone since for some reason change detection isn't performed
// as part of the XHR request to upload the files.
// Running in a custom zone forces change detection
this.zone.run(() => {
// Update an existing entry if it's upload hasn't completed yet
// Find the id of an existing item
const existingId = this.responses.reduce((prev, current, index) => {
if (current.file.name === fileItem.file.name && !current.status) {
return index;
}
return prev;
}, -1);
if (existingId > -1) {
// Update existing item with new data
this.responses[existingId] = Object.assign(this.responses[existingId], fileItem);
} else {
// Create new response
this.responses.push(fileItem);
}
});
};
// Update model on completion of uploading a file
this.uploader.onCompleteItem = (item: any, response: string, status: number, headers: ParsedResponseHeaders) =>
upsertResponse(
{
file: item.file,
status,
data: JSON.parse(response)
}
);
// Update model on upload progress event
this.uploader.onProgressItem = (fileItem: any, progress: any) =>
upsertResponse(
{
file: fileItem.file,
progress,
data: {}
}
);
}
updateTitle(value: string) {
this.title = value;
}
// Delete an uploaded image
// Requires setting "Return delete token" to "Yes" in your upload preset configuration
// See also https://support.cloudinary.com/hc/en-us/articles/202521132-How-to-delete-an-image-from-the-client-side-
deleteImage = function (data: any, index: number) {
const url = `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/delete_by_token`;
const headers = new Headers({ 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' });
const options = { headers: headers };
const body = {
token: data.delete_token
};
this.http.post(url, body, options).subscribe(response => {
console.log(`Deleted image - ${data.public_id} ${response.result}`);
// Remove deleted item for responses
this.responses.splice(index, 1);
});
};
fileOverBase(e: any): void {
this.hasBaseDropZoneOver = e;
}
getFileProperties(fileProperties: any) {
// Transforms Javascript Object to an iterable to be used by *ngFor
if (!fileProperties) {
return null;
}
return Object.keys(fileProperties)
.map((key) => ({ 'key': key, 'value': fileProperties[key] }));
}
}
这是我的 component.html
:
<h1>Image Upload to Cloudinary TESTING</h1>
<div id="direct_upload" ng2FileDrop [uploader]="uploader" (fileOver)="fileOverBase($event)" [ngClass]="{'nv-file-over': hasBaseDropZoneOver}">
<h1>New Photo</h1>
<h2>Direct upload from the browser with Angular File Upload</h2>
<p>You can also drag and drop an image file into the dashed area.</p>
<form>
<div class="form_line">
<label path="title">Title:</label>
<div class="form_controls">
<input type="text" class="form-control" #title placeholder="Title" (keyup.enter)="updateTitle(title.value)" (blur)="updateTitle(title.value)"
/>
</div>
</div>
<div class="form_line">
<label>Image:</label>
<div class="form_controls">
<div class="upload_button_holder">
<label class="upload_button" for="fileupload">Upload</label>
<!-- onChange hanlder resets the input value to get the change event when uploading the same file consecutively -->
<input type="file" id="fileupload" #fileInput ng2FileSelect [style.display]="'none'" [uploader]="uploader" (change)="fileInput.value=''"
multiple />
</div>
</div>
</div>
</form>
<h2>Status</h2>
<div class="file" *ngFor="let response of responses; let i = index">
<h3>{{response.file.name}}</h3>
<button class="delete-image" *ngIf="!!response.data.delete_token" (click)="deleteImage(response.data, i)">Delete image</button>
<div class="status">
Uploading... {{response.progress}}%
<div *ngIf="!response.status">In progress</div>
<div class="status-code" *ngIf="response.status">Upload completed with status code {{response.status}}</div>
</div>
<div class="progress-bar">
<div class="progress" role="progressbar" [style.width.%]="response.progress"></div>
</div>
<div class="form_line">
<div class="form_controls">
<div class="preview">
<!-- Consider using https://github.com/valor-software/ng2-file-upload/issues/461 for image preview -->
</div>
</div>
</div>
<div class="info">
<table>
<tr *ngFor="let property of getFileProperties(response.data)">
<td> {{ property.key }} </td>
<td> {{ property.value | json}} </td>
</tr>
</table>
</div>
</div>
</div>
在我的 app.module.ts
中,我进行了导入:
...
import { CloudinaryModule, CloudinaryConfiguration } from '@cloudinary/angular-5.x';
import { Cloudinary } from 'cloudinary-core';
import { FileUploadModule } from "ng2-file-upload";
@NgModule({
declarations: [
...
CloudinaryComponent
],
imports: [
...
// Cloudinary import
CloudinaryModule.forRoot({Cloudinary}, { cloud_name: 'CLOUDN_NAME' } as CloudinaryConfiguration),
FileUploadModule
],
providers: [
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
所以我真的不明白为什么它没有获得授权,但同样困扰我的是,在 Cloudinary 文档中 Angular 我不知道我应该把我的 API Key
和我的 API Secret
.
在我的项目中没有任何地方有像 systemjs.config.js
rollup-config.js
.
如果有人能帮助我,那就太棒了!
谢谢
在您的示例中,您需要更改云 URL
。
您有 https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload
,但您需要 https://api.cloudinary.com/v1_1/CLOUD_NAME/upload
。
你的https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/delete_by_token
也需要改成https://api.cloudinary.com/v1_1/CLOUD_NAME/delete_by_token
而您的 upload_preset
您需要将其从 form.append('upload_preset', this.cloudinary.config().upload_preset)
更改为 form.append('upload_preset', 'PRESET_NAME')
所以你的组件应该是这样的:
...
ngOnInit(): void {
// Create the file uploader, wire it to upload to your account
const uploaderOptions: FileUploaderOptions = {
url: `https://api.cloudinary.com/v1_1/${this.cloudinary.config().CLOUD_NAME}/upload`,
// Upload files automatically upon addition to upload queue
autoUpload: true,
// Use xhrTransport in favor of iframeTransport
isHTML5: true,
// Calculate progress independently for each uploaded file
removeAfterUpload: true,
// XHR request headers
headers: [
{
name: 'X-Requested-With',
value: 'XMLHttpRequest'
}
]
};
this.uploader = new FileUploader(uploaderOptions);
this.uploader.onBuildItemForm = (fileItem: any, form: FormData): any => {
// Add Cloudinary's unsigned upload preset to the upload form
form.append('upload_preset', 'PRESET_NAME');
// Add built-in and custom tags for displaying the uploaded photo in the list
let tags = 'myphotoalbum';
if (this.title) {
form.append('context', `photo=${this.title}`);
tags = `myphotoalbum,${this.title}`;
}
// Upload to a custom folder
// Note that by default, when uploading via the API, folders are not automatically created in your Media Library.
// In order to automatically create the folders based on the API requests,
// please go to your account upload settings and set the 'Auto-create folders' option to enabled.
form.append('folder', 'angular_sample');
// Add custom tags
form.append('tags', tags);
// Add file to upload
form.append('file', fileItem);
// Use default "withCredentials" value for CORS requests
fileItem.withCredentials = false;
return { fileItem, form };
};
...
// Delete an uploaded image
// Requires setting "Return delete token" to "Yes" in your upload preset configuration
// See also https://support.cloudinary.com/hc/en-us/articles/202521132-How-to-delete-an-image-from-the-client-side-
deleteImage = function (data: any, index: number) {
const url = `https://api.cloudinary.com/v1_1/CLOUD_NAME/delete_by_token`;
const headers = new Headers({ 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' });
const options = { headers: headers };
const body = {
token: data.delete_token
};
...
对于 API key
,如果您要上传到已签名的 upload_preset
,您可以像这样在您的组件中添加密钥,如 sample project angular:
...
ngOnInit(): void {
console.log("initialized");
(window as any).cloudinary.createMediaLibrary(
{
cloud_name: "<cloud name>",
api_key: "<api key>",
button_class: "myBtn",
username: "<user email>",
button_caption: "Select Image or Video"
},
...