我正在尝试创建一项服务,使我能够:
为此,我首先关注this tutorial on Medium。我已经能够将文件保存在存储器中并返回当前进度。
我想在保存文件路径的情况下调用服务时遇到问题。
我查看了以下内容,尝试找出如何实现这一目标,但没有成功making multiple http requests,the rxjs docs making subsequent http requests。
我正在努力了解如何将示例更改为可以使用的内容。
请注意,我正在尝试使azureBlobStorageService可重用,因此,我没有在azure服务中进行第二个HTTP调用,这将由调用方负责。
下面是我的代码,其中我尝试添加mergeMap或flatMap等,但是没有成功。我尝试了许多变体后就删除了这些参考,我觉得评论可以更清楚地了解我要实现的目标
上传组件
class Attachment < ApplicationRecord
include Rails.application.routes.url_helpers
has_one_attached :file
....
def with_uploaded_file
tempfile = Tempfile.open([file.filename.to_s, File.extname(file.filename.to_s)]) do |file_temp|
file_temp.binmode unless file.content_type =~ /text/
require 'open-uri'
# file_temp.write(open(file.service_url).read)
file_temp.write(open(rails_representation_url(file, only_path: true)).read)
file_temp
end
begin
yield(tempfile)
ensure
tempfile.unlink
end
上传服务
this.uploadProgress$ = from(inputNode.files as FileList).pipe(
map(file => this._uploadService.updateCertificate(file)),
combineAll()
);
Azure BlobStorage服务
// this is where I would like to call my REST api when the file has uploaded to azure
updateCertificate(file: File): Observable<IUploadProgress> {
return this._azureBlobStorage
.uploadCertificateToBlobStorage(file, this.group)
.pipe(
map(
progress => this.mapProgress(file, progress)
),
// flatMap(x => this._httpClient.post('xcv', JSON.Stringify(sasToken.filename))) <--fail 1
)
.pipe(flatMap(x => this._httpClient.post('', JSON.stringify('')))); <-- fail 2
} // also tried merge map and a couple of others
private mapProgress(file: File, progress: number): IUploadProgress {
return {
filename: file.name,
progress: progress
};
}
===============================
更改为上传文件
以下原因
Observable类型不能分配给Observable类型
===============================
uploadCertificateToBlobStorage(file: File, group: string): Observable<number>
{
this.populateSasToken('/cert/' + group + '/' + file.name);
return this.uploadToBlobStorage(this.sasToken, file);
}
private populateSasToken(filename: string): void {
//create sasToken stuff
}
private uploadToBlobStorage(sasToken: ISasToken, file: File): Observable<number> {
const customBlockSize = this.getBlockSize(file);
const options = { blockSize: customBlockSize };
const blobService = this.createBlobService(sasToken.storageAccessToken, sasToken.storageUri);
blobService.singleBlobPutThresholdInBytes = customBlockSize;
return this.uploadFile(blobService, sasToken, file, options);
}
private createBlobService(sasToken: string, blobUri: string): IBlobService {
return this._blobStorage
.createBlobServiceWithSas(blobUri, sasToken)
.withFilter(new this._blobStorage.ExponentialRetryPolicyFilter());
}
// Need to change this to return a custom object with number and the sasToken.filename
// but when I change this return type and the return of the associated methods I errors, I can't see what i'm missing
private uploadFile(
blobService: IBlobService,
sasToken: ISasToken,
file: File,
options: { blockSize: number }
): Observable<number> {
return new Observable<number>(observer => {
const speedSummary = blobService.createBlockBlobFromBrowserFile(
sasToken.container,
sasToken.filename,
file,
options,
error => this.callback(error, observer)
);
speedSummary.on('progress', () => this.getProgress(speedSummary, observer, sasToken.filename));
}).pipe(
startWith(0),
distinctUntilChanged()
// retry(4) I think this will allow me to retry failed called to azure.
);
}
private getProgress(speedSummary: ISpeedSummary, observer: Subscriber<number>, fileName: string): void {
const progress = parseInt(speedSummary.getCompletePercent(2), 10);
observer.next(progress === 100 ? 99 : progress);
}
private callback(error: any, observer: Subscriber<number>): void {
if (error) {
console.log(error);
observer.error(error);
} else {
observer.next(100);
observer.complete();
}
}
答案 0 :(得分:0)
我使用了Data memory unit
自从我看了这段代码已经有一段时间了,但是这里有一些代码片段可能会有所帮助
file.service.ts
import * as FileSaver from 'file-saver';
import { Injectable } from '@angular/core';
import { ProgressHttp, Progress } from "angular-progress-http";
import { RequestOptions, Headers, Response, ResponseContentType } from '@angular/http';
import { AuthHttp } from 'angular2-jwt';
import { Observable } from 'rxjs/Observable';
import { environment } from '../environments/environment';
@Injectable()
export class FileService {
constructor(private http: ProgressHttp, private authHttp: AuthHttp) { }
upload(url: string, files: File[], listener: (progress: Progress) => void): Observable<Response> {
let formData: FormData = new FormData();
files.forEach(file => {
if (file) {
formData.append('uploadFile', file, file.name);
}
});
let headers = new Headers();
headers.append('Authorization', `Bearer ${localStorage.getItem('token')}`);
let options = new RequestOptions({ headers: headers });
return this.http.withUploadProgressListener(listener).post(url, formData, options);
}
download(url: string, filename: string) {
let options = new RequestOptions(new Headers({ 'Content-Type': 'application/json' }));
options.responseType = ResponseContentType.Blob;
this.authHttp.get(url, options).subscribe(r => {
this.saveFileContent(r, filename);
});
}
private saveFileContent(res: Response, filename: string) {
let fileBlob = res.blob();
let blob = new Blob([fileBlob]);
FileSaver.saveAs(blob, filename);
}
}
和api端点操作。
[Authorize(Roles = "Administrator"), HttpPost("AddFile/{id}")]
public async Task<IActionResult> AddFile(int id)
{
var files = Request.Form.Files;
if (files.Count > 0)
{
var sectionId = dbContext.Articles.Where(a => a.Id == id).Select(a => a.SectionId).Single();
using (var fileStream = files[0].OpenReadStream())
{
await fileService.SaveAsync($"sections/{sectionId}/articles/{id}/{files[0].FileName}", fileStream);
}
}
return Content("Ok");
}
和文件服务
using ContactManager.API.Models;
using Microsoft.Extensions.Options;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace ContactManager.API.Storage
{
public class AzureFileService : IFileService
{
AppSettings appSettings;
CloudStorageAccount storageAccount = null;
CloudStorageAccount StorageAccount
{
get
{
if (storageAccount == null)
{
storageAccount = new CloudStorageAccount(new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(this.appSettings.AzureStorage.Account, this.appSettings.AzureStorage.Key), true);
}
return storageAccount;
}
}
CloudBlobClient blobClient = null;
CloudBlobClient BlobClient
{
get
{
if (blobClient == null)
{
blobClient = StorageAccount.CreateCloudBlobClient();
}
return blobClient;
}
}
private CloudBlobContainer GetContainerReference(Permission permission)
{
return BlobClient.GetContainerReference(permission == Permission.Public ? appSettings.AzureStorage.PublicFolder : appSettings.AzureStorage.PrivateFolder);
}
public AzureFileService(IOptions<AppSettings> appSettings)
{
this.appSettings = appSettings.Value;
}
public async Task SaveAsync(string path, Stream stream, Permission permission = Permission.Public)
{
var container = GetContainerReference(permission);
var blockBlob = container.GetBlockBlobReference(path);
await blockBlob.UploadFromStreamAsync(stream);
}
public void Delete(string path, Permission permission = Permission.Public)
{
var container = GetContainerReference(permission);
var blockBlob = container.GetBlockBlobReference(path);
blockBlob.DeleteAsync();
}
public async Task<Stream> GetAsync(string path, Permission permission = Permission.Public)
{
var container = GetContainerReference(permission);
var blockBlob = container.GetBlockBlobReference(path);
var stream = new MemoryStream();
await blockBlob.DownloadToStreamAsync(stream);
stream.Position = 0;
return stream;
}
}
}
希望这会为您提供帮助。
答案 1 :(得分:0)
对不起,有点晚了,但是您上载方法中的this._httpClient.post('xcv', JSON.Stringify(sasToken.filename))
似乎没有返回IUploadResponse。您需要映射HttpClient调用的响应以匹配IUploadResponse接口
我还更新了说明中提到的文章,以使用较新的库-https://medium.com/@stuarttottle/upload-to-azure-blob-storage-with-angular-8-2ed80dfc6672