当前位置: 首页 > 知识库问答 >
问题:

Angular 7使用进度指示器上载到blob存储

濮阳安澜
2023-03-14

我正在尝试创建一项服务,该服务将允许我:

  1. 将文件上传到Azure blob存储
  2. 返回进度
  3. 重试失败时
  4. 成功时调用我的服务保存文件路径

为了实现这一点,我开始按照本教程中的媒体。我已经能够在存储器中保存文件并返回当前进度。

当我想调用保存了文件路径的服务时,问题就来了。

我已经看了下面的内容来尝试解决如何实现这一点,但是没有成功地发出多个http请求,rxjs文档会发出后续的http请求

我正在努力理解我是如何将这些例子变成我可以使用的东西的。

请注意,我正在尝试使azureBobStorageService可重用,因此我不会在azure服务中进行第二次HTTP调用,这将是调用方的责任。

下面是我的代码和注释,我试图添加合并地图或平面地图等,但没有成功。我已经删除了这些参考因为我已经尝试了这么多的变化我觉得评论给一个更清晰的画面我试图实现

上载组件

this.uploadProgress$ = from(inputNode.files as FileList).pipe(
  map(file => this._uploadService.updateCertificate(file)),
  combineAll()
);

上传服务

// 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
    };
  }

Azure BlobStorage服务

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();
    }
  }

================================

上传文件的更改

原因如下

类型Observable不可分配给类型Observable

================================

export class Xxx {
  y: number;
  x: string;
}




private uploadFile(
    blobService: IBlobService,
    sasToken: ISasToken,
    file: File,
    options: { blockSize: number }
  ): Observable<Xxx> {
    return new Observable<Xxx>(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)
    );
  }

  private getProgress(speedSummary: ISpeedSummary, observer: Subscriber<Xxx>, fileName: string): void {
    const progress = parseInt(speedSummary.getCompletePercent(2), 10);
    // observer.next(progress === 100 ? 99 : progress);
    observer.next(new Xxx());
  }

  private callback(error: any, observer: Subscriber<Xxx>): void {
    if (error) {
      console.log(error);
      observer.error(error);
    } else {
      // observer.next(100);
      observer.next(new Xxx());
      observer.complete();
    }
  }

共有2个答案

漆雕欣德
2023-03-14

抱歉,有点晚了,但看起来像是您的这个_httpClient。upload方法中的post('xcv',JSON.Stringify(sasToken.filename))返回IUploadResponse。您需要映射来自HttpClient调用的响应,以匹配IUploadResponse接口

我还更新了描述中提到的文章,以使用更新的库-https://medium.com/@stuarttottle/upload-to-azure-blob-storage-with-angular-8-2ed80dfc6672

诸葛奇玮
2023-03-14

我以前https://npmjs.com/package/angular-progress-http

我已经有一段时间没有看这段代码了,但这里有一些代码片段可能会有所帮助

文件服务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);
  }
}

以及apiendpoint操作。

    [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;
        }
    }
}

希望这能给你一个帮助。

 类似资料:
  • 我可以使用以下C代码将文件上载到azure blob存储, 这里我提供blob存储连接字符串和容器名称。 现在,我可以看到我在下有URL, 问题,我可以编写C代码,使用上面的URL上传文件,而不是使用连接字符串/容器名称吗?

  • 主要内容:创建ProgressIndicator进度指示器()以动态更改饼图的形式显示JavaFX中的操作进度。以下代码显示如何使用不确定值创建。 上面的代码生成以下结果。 创建ProgressIndicator 以下代码通过传递值来创建。 可以使用空构造函数创建没有参数的进度指示器。然后可以使用方法分配值。 如果无法确定进度,可以在不确定模式下设置进度控制,直到确定任务的长度。 以下代码显示如何创建一个完成25%的。 上面的代码生成以下结果。

  • 问题内容: 我正在努力寻找使用fetch实现上传进度指示器的文档或示例。 进度事件是一项高级功能,暂时无法获取。您可以通过查看标头并使用直通流来监视接收到的字节来创建自己的文件。 这意味着您可以显式地处理响应,而无需进行其他操作。当然,即使存在,也可能是谎言。使用流,您可以根据需要处理这些谎言。 我将如何编写发送的“用于监视字节的直通流”?如果有什么不同,我正在尝试执行此操作以增强从浏览器到Clo

  • 我在尝试将文件上载到blob存储时遇到此错误。在本地主机上运行和在Azure函数中运行时都会出现错误。 我的连接字符串如下所示:DefaultEndpoint sProtocol=https; AcCountName=xxx; AcCountKey=xxx; Endpoint Suffix=core.windows.net 身份验证信息的格式不正确。检查授权标头的值。时间:2021 10月14日1

  • 我使用android应用程序中的以下代码将blob上传到Azure blob存储。注意:下面的参数是从我的web服务获取的签名url: 该代码对于小的blob来说运行良好,但是当blob达到一定的大小(取决于我测试的手机)时,我开始出现内存不足的异常。我想拆分这些blob并将它们分块上传。然而,我在网上找到的所有示例都是基于C#的,并且都在使用存储客户端库。我正在寻找一个Java/Android示

  • 嗨,我是一名移动应用程序开发人员,对Web开发不太熟悉,我正在寻找在加载像Gmail加载屏幕这样的flutter Web应用程序之前实现进度指示器的任何方法。Flutter web很酷,但在加载应用程序之前需要几分钟。我们可以为此加载持续时间添加任何指标吗?在flutter中实现的任何代码都将是flutter应用程序的一部分,它不起作用,应该有另一种方法来实现这一点。