Azure Rest api put blob

问题描述 投票:0回答:3

我正在尝试使用 Azure Rest API 放置一个 Blob。我成功发出了“GET”请求,但“PUT”请求出现了问题。当我尝试发出“PUT”请求时,我收到 404 错误(我在 stackoverflow 中看到过相同的帖子,但它对我没有帮助)。我不确定我使用的 MessageSignature 是否正确(我尝试过 MessageSignaturePut 但没有工作)。有什么建议吗?

public void UploadBlobWithRestAPI(string uri,  DateTime now)
{
    string blobName = "test.txt";
    string method = "PUT";
    string sampleContent = "This is sample text.";
    int contentLength = Encoding.UTF8.GetByteCount(sampleContent);
    string queryString = (new Uri(uri)).Query;
    string blobContainerUri = uri.Substring(0, uri.Length - queryString.Length);
    string requestUri = string.Format(CultureInfo.InvariantCulture, "{0}/{1}{2}", blobContainerUri, blobName, queryString);
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);
    string nnow = now.ToString("R", System.Globalization.CultureInfo.InvariantCulture);

    request.Method = method;
    request.Headers.Add("x-ms-version", "2015-02-21");
    request.Headers.Add("x-ms-date", nnow);
    request.ContentType = "text/plain; charset=UTF-8";
    request.Headers.Add("x-ms-blob-type", "BlockBlob");
    request.ContentLength = contentLength;

    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(Encoding.UTF8.GetBytes(sampleContent), 0, contentLength);
    }

    request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, "", ""));

    using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse())
    {
        MessageBox.Show(resp.StatusCode.ToString());
    }
}


public string AuthorizationHeader(string method, DateTime now, HttpWebRequest request,
        string ifMatch = "", string md5 = "")
{
    string MessageSignature;
    string StorageKey = "xxx";
    string StorageAccount = "upgradedevstorage";

    MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}",
        method,
        (method == "GET" || method == "HEAD") ? String.Empty : request.ContentLength.ToString(),
        ifMatch,
        GetCanonicalizedHeaders(request),
        GetCanonicalizedResource(request.RequestUri, StorageAccount),
        md5
        );

 ???   //string MessageSignaturePut= String.Format("{0}\n\n{1}\n\n{2}{3}",
    //    method,
    //    "text/plain; charset=UTF-8",
    //    GetCanonicalizedHeaders(request),
    //    GetCanonicalizedResource(request.RequestUri, StorageAccount)
    //    );

    byte[] SignatureBytes = System.Text.Encoding.UTF8.GetBytes(MessageSignature);

    System.Security.Cryptography.HMACSHA256 SHA256 =
        new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(StorageKey));

    string signature = Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));

    string AuthorizationHeader = "SharedKey " + StorageAccount
        + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));

    return AuthorizationHeader;
}
c# rest azure azure-storage azure-blob-storage
3个回答
9
投票

请考虑以下代码片段。这应该对你有用。

void UploadBlobWithRestAPI() {

    string storageKey = "<your access key here>";
    string storageAccount = "<your storage account name here>";    
    string containerName = "<your container name here>";
    string blobName = "test.txt";

    string method = "PUT";
    string sampleContent = "This is sample text.";
    int contentLength = Encoding.UTF8.GetByteCount(sampleContent);

    string requestUri = $"https://{storageAccount}.blob.core.windows.net/{containerName}/{blobName}";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri);

    string now = DateTime.UtcNow.ToString("R");

    request.Method = method;
    request.ContentType = "text/plain; charset=UTF-8";
    request.ContentLength = contentLength;

    request.Headers.Add("x-ms-version", "2015-12-11");
    request.Headers.Add("x-ms-date", now);
    request.Headers.Add("x-ms-blob-type", "BlockBlob");
    request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, storageAccount, storageKey, containerName, blobName));

    using (Stream requestStream = request.GetRequestStream()) {
        requestStream.Write(Encoding.UTF8.GetBytes(sampleContent), 0, contentLength);
    }

    using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse()) {
        MessageBox.Show(resp.StatusCode.ToString());
    }

}

public string AuthorizationHeader(string method, string now, HttpWebRequest request, string storageAccount, string storageKey, string containerName, string blobName) {

    string headerResource = $"x-ms-blob-type:BlockBlob\nx-ms-date:{now}\nx-ms-version:2015-12-11";
    string urlResource = $"/{storageAccount}/{containerName}/{blobName}";
    string stringToSign = $"{method}\n\n\n{request.ContentLength}\n\n{request.ContentType}\n\n\n\n\n\n\n{headerResource}\n{urlResource}";

    HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(storageKey));
    string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));

    String AuthorizationHeader = String.Format("{0} {1}:{2}", "SharedKey", storageAccount, signature);
    return AuthorizationHeader;
}

Fiddler抓取到的流量如下:

enter image description here


1
投票

上述解决方案仅上传最大 4MB 的文件。我需要一个 PowerShell 版本来满足项目需求,并且使用上面的解决方案走上了错误的道路。对于将 BLOB 分块到 Azure RBS 的自定义函数,我修改了 Red Gate 的 Robin Shahan 版本,网址为 https://www.red-gate.com/simple-talk/cloud/platform-as-a-service/azure- blob-存储-第 4 部分-上传-大-blob/.

$sdkPath = "C:/Program Files/Microsoft SDKs/Azure/.NET SDK/v2.9/bin/plugins/Diagnostics/Microsoft.WindowsAzure.Storage.dll"
[System.Reflection.Assembly]::LoadFrom($sdkPath);

Add-Type -AssemblyName System.Net.Http

function getMD5HashFromBytes([byte[]]$fileBytes){
    $md5 = [System.Security.Cryptography.MD5]::Create()
    [byte[]]$hash = $md5.ComputeHash($fileBytes)
    return [System.Convert]::ToBase64String($hash)
}

function setupBlobContainer($account, $secretKey, $container){
    $cs = [String]::Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", $account, $secretKey)
    $cloudStorageAccount = [Microsoft.WindowsAzure.Storage.CloudStorageAccount]::Parse($cs)
    $cloudBlobClient = [Microsoft.WindowsAzure.Storage.Blob.CloudBlobClient]$cloudStorageAccount.CreateCloudBlobClient()
    $cloudBlobContainer = [Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer]$cloudBlobClient.GetContainerReference($container)
    return $cloudBlobContainer
}

function chunkBlob([string]$filepath, [string]$filename, `
    [Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer]$cloudBlobContainer){

    #ref: https://www.red-gate.com/simple-talk/cloud/platform-as-a-service/azure-blob-storage-part-4-uploading-large-blobs/

    $blob = [Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob]$cloudBlobContainer.GetBlockBlobReference($filename)
    $blockSize = 256 * 1024; #256 kb
    $fileStream = [System.IO.FileStream]::new($filepath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
    $fileSize = $fileStream.Length

    #block count is the number of blocks + 1 for the last one
    $blockCount = [int]([float]$fileSize / [float]$blockSize) + 1
 
    #List of block ids; the blocks will be committed in the order of this list 
    $blockIDs = [System.Collections.Generic.List[string]]::new()
 
    #starting block number - 1
    $blockNumber = 0
 
    try
    {
        $bytesRead = 0 #number of bytes read so far
        $bytesLeft = $fileSize; #number of bytes left to read and upload
 
        #do until all of the bytes are uploaded
        while($bytesLeft -gt 0){

            $blockNumber++;

            [int]$bytesToRead;
        
            if($bytesLeft -ge $blockSize){
                #more than one block left, so put up another whole block
                $bytesToRead = $blockSize
            }
            else{
                #less than one block left, read the rest of it
                $bytesToRead = [int]$bytesLeft
            }
 
            #create a blockID from the block number, add it to the block ID list
            #the block ID is a base64 string
            $blockId = [Convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes([String]::Format("BlockId{0}", $blockNumber.ToString("0000000"))))
            $blockIDs.Add($blockId)
        
            #set up new buffer with the right size, and read that many bytes into it 
            [byte[]]$bytes = [System.Byte[]]::new($bytesToRead)

            $fileStream.Read($bytes, 0, $bytesToRead)
 
            #calculate the MD5 hash of the byte array
            $blockHash = getMD5HashFromBytes $bytes
 
            #upload the block, provide the hash so Azure can verify it
            $blob.PutBlock($blockId, [System.IO.MemoryStream]::new($bytes), $blockHash)
 
            #increment/decrement counters
            $bytesRead += $bytesToRead
            $bytesLeft -= $bytesToRead

            $perc = [float][math]::Round( [float]$bytesRead/[float]($bytesRead + $bytesLeft) * 100, 2)

            Write-Progress -Activity "Writing '$($filename)'..." -PercentComplete $perc
        }
 
        #commit the blocks
        $blob.PutBlockList($blockIDs)
    }
    catch [System.Exception] {
        write-warning $_
    }
    finally{
        if($fileStream){
            $fileStream.Dispose()
        }
    }
}

0
投票

添加@John Bonfardeci的答案,如果您不想使用azure sdk,这将适用于powershell。

function Add-FileToBlobStorage {
    param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName)]
        [ValidateScript({Test-Path $_ })]
        [Alias("FullName")]
        [string] $Path,
        [string] $TargetFileName,
        [Parameter(Mandatory=$true)]
        [ValidateScript({($_ -match "^https:\/\/(.)*\.blob.core.windows.net\/(.)*") -and ($_ -notmatch '\?')})]
        [string] $containerUrl,
        [Parameter(Mandatory=$true)]
        [ValidateScript({$_.StartsWith("?")})]
        [string]$sasToken
    )
    if (-not $TargetFileName) {
        $FileItem = Get-Item $Path -ErrorAction Stop
        $TargetFileName=$FileItem.Name
    }
    $HashArguments = @{
        uri = "$containerUrl/$($TargetFileName.Replace("\","/"))$sasToken"
        method = "Put"
        InFile = $Path
        headers = @{
            "x-ms-blob-type" = "BlockBlob"
        }
    }
    Write-Verbose "Uploading to $containerUrl/$($TargetFileName.Replace("\","/")) from $path"
    Invoke-RestMethod @HashArguments
    Write-Host "Upload to $containerUrl/$($TargetFileName.Replace("\","/")) from $path was successful"
}

# Usage example:
$url = "https://$storageAccountName.blob.core.windows.net/$containerName"
Get-Item $localFilePath | Add-FileToBlobStorage -containerUrl $url -sasToken $sasToken -TargetFileName 'a\test.html'

© www.soinside.com 2019 - 2024. All rights reserved.