【问题标题】:How to pass Forge bucket download *around* web app, not pass it *through* web app?如何通过 Forge 存储桶下载 *around* Web 应用程序,而不是 *通过* Web 应用程序?
【发布时间】:2020-01-13 22:34:17
【问题描述】:

我有一个基于此示例的 Azure 托管 Web 应用:https://github.com/Autodesk-Forge/data.management-csharp-desktop.sample

此示例演示了一个桌面应用程序,该应用程序显示了 BIM 360 Team、BIM 360 Docs 和 Fusion Team 中心,它们各自的项目、文件夹、项目和版本。每个版本都可以使用 Viewer 查看。

(出于安全目的,托管应用程序用作中介。)

我已扩展此示例,以便可以将项目和版本对象下载到桌面。这行得通。

要下载的对象/文件从 Autodesk 服务器检索为 HttpResponseMessage

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Autodesk.Forge;

namespace FPD.Sample.Cloud.Controllers
{
    public class DownloadController : ApiController
    {
        private const string FORGE_BASE_URL = "https://developer.api.autodesk.com";
        private const string PROXY_ROUTE = "api/forge/download/";

        // HttpClient has been designed to be re-used for multiple calls. 
        // Even across multiple threads. 
        // https://stackoverflow.com/a/22561368/4838205
        private static HttpClient _httpClient;

        [HttpGet]
        [Route("api/forge/download/{bucketKey}/{objectName}/bucket")]
        public async Task<HttpResponseMessage> Get(string bucketKey, string objectName)
        {
            string sessionId, localId;
            if (!HeaderUtils.GetSessionLocalIDs(out sessionId, out localId)) return null;
            if (!await OAuthDB.IsSessionIdValid(sessionId, localId)) return null;
            string userAccessToken =  await OAuthDB.GetAccessToken(sessionId, localId);


            if (_httpClient == null)
            {
                _httpClient = new HttpClient(
                  // this should avoid HttpClient searching for proxy settings
                  new HttpClientHandler()
                  {
                      UseProxy = false,
                      Proxy = null
                  }, true);
                _httpClient.BaseAddress = new Uri(FORGE_BASE_URL);
                ServicePointManager.DefaultConnectionLimit = int.MaxValue;
            }

            string resourceUrl = $"/oss/v2/buckets/{bucketKey}/objects/{objectName}";
            try
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, resourceUrl);

                // add our Access Token
                request.Headers.Add("Authorization", "Bearer " + userAccessToken);

                HttpResponseMessage response = await _httpClient.SendAsync(request,
                  // this ResponseHeadersRead force the SendAsync to return
                  // as soon as the header is ready, faster
                HttpCompletionOption.ResponseHeadersRead);

                return response;
            }
            catch
            {
                return new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);
            }
        }
    }
}

这个响应然后被传递到桌面应用程序,然后转换为字节:

        public async static Task<byte[]> GetDownload(string  bucketKey, string objectName)
        {
            byte[] bytes = await RestAPI<byte[]>.RequestAsyncBytes("api/forge/download/" + bucketKey + "/" + objectName +  "/bucket", true);
            return bytes;
        }
        public static async Task<byte[]> RequestAsyncBytes(string endpoint, IDictionary<string, string> headers, bool includeIdHeaders)
        {
            var client = new RestClient(EndPoints.BaseURL);
            var request = new RestRequest(endpoint, Method.GET);

            if (includeIdHeaders)
            {
                if (headers == null) headers = new Dictionary<string, string>();
                headers.Add("FPDSampleSessionId", SessionManager.SessionId);
                headers.Add("FPDSampleLocalId", SessionManager.MachineId);
            }

            if (headers != null)
                foreach (KeyValuePair<string, string> header in headers)
                    request.AddHeader(header.Key, header.Value);

            IRestResponse response = await client.ExecuteTaskAsync(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                return response.RawBytes; 
            }
            else if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
            {
                throw new Exception("No Content: " + endpoint);

            }
            else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
            {
                throw new Exception("Not found: " + endpoint);

            }
            else
            {
                throw new Exception($"Error {response.StatusCode}. Cannot call {endpoint}");
            }

        }

然后以文件的形式保存到硬盘,使用如下方式:

byte [] bytes = await Forge.DataManagement.GetDownload(bucketKey, objectName);
System.IO.File.WriteAllBytes(pathAndFilename, bytes);

是否可以在 Azure 应用程序周围传递大部分有效负载 以便对象/文件完好无损地到达,保持安全性,但应用程序的数据限额不会被耗尽?

【问题讨论】:

  • 您可以尝试从 rest API 返回不是从存储桶下载的内容,而是一个元组 (bucketUri, userAccessToken) 。然后使用来自 REST API 的相同代码在桌面客户端上执行存储桶的下载
  • @dvitel 这将涉及将 accessToken 传递给桌面客户端,不是吗?我试图避免这种情况。

标签: c# download autodesk-forge asp.net-apicontroller autodesk-data-management


【解决方案1】:

您可以为该对象创建一个Signed URL 并将其传递到您的后端,基本上一个签名的 URL 是一个允许临时匿名访问的对象的链接 - 有关详细信息,请参阅 here

或者,您可以将对象下载到临时位置(请参阅herehere)并传递路径信息,这样可以节省代码的其他部分再次下载它的时间。

【讨论】:

  • 谢谢你,@bryan。我尝试了签名 URL 方法,但得到了 403 禁止响应。 (我将范围更改为包含 data:write,使用 POST 方法,并使用签名端点。)可能是因为:“成功调用此端点需要存储桶所有者访问权限。”我正在尝试下载模型、衍生产品等。您提到的替代方案与我当前的工作流程没有太大区别;它仍然涉及将数据带到内存或虚拟磁盘中的临时点。
  • 在为对象创建签名 URL 时,确保使用客户端 ID/秘密的 Forge 应用拥有目标存储桶 - 刚刚尝试在我的存储桶中的一个对象上运行:{ "signedUrl": "https://developer.api.autodesk.com/oss/v2/signedresources/7012e598-dc2b-4aee-b8df-8ed968021170?region=US", "expiration": 1568603960947, "singleUse": false }
  • 我想将它用于 BIM360 模型和衍生产品,所以我绝对不会成为桶的所有者。还有其他想法吗?
  • 还有什么想法吗?
  • 让我与工程部谈谈,看看是否有办法解决权限问题。
猜你喜欢
  • 2011-04-30
  • 2014-12-21
  • 2012-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2018-12-14
  • 1970-01-01
相关资源
最近更新 更多