66using Azure ;
77using Azure . Storage . Blobs ;
88using Azure . Storage . Blobs . Models ;
9+ using Azure . Storage . Sas ;
910using Microsoft . Extensions . Options ;
1011using StardewModdingAPI . Web . Framework . Clients . Pastebin ;
1112using StardewModdingAPI . Web . Framework . Compression ;
@@ -53,16 +54,14 @@ public StorageProvider(IOptions<ApiClientsConfig> clientsConfig, IPastebinClient
5354 }
5455
5556 /// <inheritdoc />
56- public async Task < UploadResult > SaveAsync ( string content , bool compress = true )
57+ public async Task < UploadResult > SaveAsync ( string id , string content , bool compress = true )
5758 {
58- string id = Guid . NewGuid ( ) . ToString ( "N" ) ;
59-
6059 // save to Azure
6160 if ( this . HasAzure )
6261 {
6362 try
6463 {
65- using Stream stream = new MemoryStream ( Encoding . UTF8 . GetBytes ( content ) ) ;
64+ await using Stream stream = new MemoryStream ( Encoding . UTF8 . GetBytes ( content ) ) ;
6665 BlobClient blob = this . GetAzureBlobClient ( id ) ;
6766 await blob . UploadAsync ( stream ) ;
6867
@@ -80,42 +79,53 @@ public async Task<UploadResult> SaveAsync(string content, bool compress = true)
8079 string path = this . GetDevFilePath ( id ) ;
8180 Directory . CreateDirectory ( Path . GetDirectoryName ( path ) ! ) ;
8281
83- File . WriteAllText ( path , content ) ;
82+ await File . WriteAllTextAsync ( path , content ) ;
8483 return new UploadResult ( id , null ) ;
8584 }
8685 }
8786
8887 /// <inheritdoc />
89- public async Task < StoredFileInfo > GetAsync ( string id , bool forceRenew )
88+ public async Task < StoredFileInfo > GetAsync ( string id , bool forceRenew , bool forceDownloadContent = false )
9089 {
9190 // fetch from blob storage
92- if ( Guid . TryParseExact ( id , "N" , out Guid _ ) )
91+ bool isBlobStorage = id . StartsWith ( "parsed-" )
92+ ? Guid . TryParseExact ( id . Substring ( "parsed-" . Length ) , "N" , out _ )
93+ : Guid . TryParseExact ( id , "N" , out _ ) ;
94+ if ( isBlobStorage )
9395 {
9496 // Azure Blob storage
9597 if ( this . HasAzure )
9698 {
9799 try
98100 {
99- // get client
100- BlobClient blob = this . GetAzureBlobClient ( id ) ;
101-
102- // fetch file
103- Response < BlobDownloadInfo > response = await blob . DownloadAsync ( ) ;
104- using BlobDownloadInfo result = response . Value ;
105- using StreamReader reader = new ( result . Content ) ;
106- DateTimeOffset oldExpiry = this . GetExpiry ( result . Details . LastModified ) ;
107- string content = this . GzipHelper . DecompressString ( await reader . ReadToEndAsync ( ) ) ;
101+ // fetch metadata
102+ BlobClient blobClient = this . GetAzureBlobClient ( id ) ;
103+ Response < BlobProperties > properties = await blobClient . GetPropertiesAsync ( ) ;
104+ DateTimeOffset lastModified = properties . Value . LastModified ;
105+ DateTimeOffset oldExpiry = this . GetExpiry ( lastModified ) ;
106+
107+ // get content or URL file
108+ string ? content = null ;
109+ string ? fetchUri = null ;
110+ if ( forceDownloadContent )
111+ {
112+ Response < BlobDownloadInfo > response = await blobClient . DownloadAsync ( ) ;
113+ using StreamReader reader = new ( response . Value . Content ) ;
114+ content = this . GzipHelper . DecompressString ( await reader . ReadToEndAsync ( ) ) ;
115+ }
116+ else
117+ fetchUri = blobClient . GenerateSasUri ( BlobSasPermissions . Read , DateTimeOffset . UtcNow . AddMinutes ( 5 ) ) . ToString ( ) ;
108118
109119 // extend expiry if needed
110120 DateTimeOffset newExpiry = oldExpiry ;
111- if ( forceRenew || this . IsWithinAutoRenewalWindow ( result . Details . LastModified ) )
121+ if ( forceRenew || this . IsWithinAutoRenewalWindow ( lastModified ) )
112122 {
113- await blob . SetMetadataAsync ( new Dictionary < string , string > { [ "expiryRenewed" ] = DateTime . UtcNow . ToString ( "O" ) } ) ; // change the blob's last-modified date (the specific property set doesn't matter)
123+ await blobClient . SetMetadataAsync ( new Dictionary < string , string > { [ "expiryRenewed" ] = DateTime . UtcNow . ToString ( "O" ) } ) ; // change the blob's last-modified date (the specific property set doesn't matter)
114124 newExpiry = this . GetExpiry ( DateTimeOffset . UtcNow ) ;
115125 }
116126
117127 // build model
118- return new StoredFileInfo ( content , oldExpiry , newExpiry ) ;
128+ return new StoredFileInfo ( fetchUri , content , oldExpiry , newExpiry ) ;
119129 }
120130 catch ( RequestFailedException ex )
121131 {
@@ -135,9 +145,7 @@ public async Task<StoredFileInfo> GetAsync(string id, bool forceRenew)
135145 if ( file . Exists && file . LastWriteTimeUtc . AddDays ( this . ExpiryDays ) < DateTime . UtcNow ) // expired
136146 file . Delete ( ) ;
137147 if ( ! file . Exists )
138- {
139148 return new StoredFileInfo ( error : "There's no file with that ID." ) ;
140- }
141149
142150 // renew
143151 if ( forceRenew )
@@ -148,7 +156,8 @@ public async Task<StoredFileInfo> GetAsync(string id, bool forceRenew)
148156
149157 // build model
150158 return new StoredFileInfo (
151- content : await File . ReadAllTextAsync ( file . FullName ) ,
159+ fetchUri : null ,
160+ fetchedData : await File . ReadAllTextAsync ( file . FullName ) ,
152161 oldExpiry : null ,
153162 newExpiry : DateTime . UtcNow . AddDays ( this . ExpiryDays ) ,
154163 warning : "This file was saved temporarily to the local computer. This should only happen in a local development environment."
@@ -161,7 +170,7 @@ public async Task<StoredFileInfo> GetAsync(string id, bool forceRenew)
161170 {
162171 PasteInfo response = await this . Pastebin . GetAsync ( id ) ;
163172 response . Content = this . GzipHelper . DecompressString ( response . Content ) ;
164- return new StoredFileInfo ( response . Content , null , null , error : response . Error ) ;
173+ return new StoredFileInfo ( null , response . Content , null , null , error : response . Error ) ;
165174 }
166175 }
167176
0 commit comments