Migrate from v5 to v6
This guide will help you migrate Amplify JavaScript v5's Storage APIs to the new v6's Storage APIs.
Removed Options
The input options below have been removed in v6 from Storage APIs:
provider
: Custom providers are no longer supported in v6. All v5 examples below use the input/output values expected for the default S3 Provider.customPrefix
: You can add a prefix resolver to your Storage Config to customize the key prefix per accessLevel. See Using a Custom Prefix for migration details.track
: Auto-tracking Storage events in pinpoint has been removed in V6. You can track Storage events using the Analytics category. See Tracking storage events for migration details.ServerSideEncryption
,SSECustomerAlgorithm
,SSECustomerKey
,SSECustomerKeyMD5
, andSSEKMSKeyId
: These options have been deprecated because Server-Side Encryption is automatically applied using default keys by S3 since Jan 2023.cacheControl
: This option has been removed and now defers to the HTTP caching behavior configured by the runtime.
bucket
: Thebucket
is required forStorage
configuration object as part ofAmplify.configure()
and has therefore been removed from input options.
Note: We have remapped all capitalized input and output keys to "camelCase" in v6.
Deprecated Options
Starting with version 6.2.0, the following API parameters and output options have been marked as deprecated and may be removed in a future version.
-
key
: A newpath
parameter has been introduced and will be used instead of thekey
parameter in input and output options. Thepath
parameter allows for flexible path construction based on user's identity id. -
accessLevel
: FileaccessLevel
with scopedpublic
,protected
, andprivate
permissions is now deprecated. Instead, Amplify (Gen2) introduces a more refined access control mechanism. To offer similar flexibility to clients, we will utilize thepath
parameter for input options.
Storage.put
The Storage.put
API has been renamed to uploadData
in v6. There are a few changes in the experience from v5 to v6 in addition to those mentioned above:
- We removed the
ACL
option: the S3 Bucket ACL provides extra access control options that Amplify does not currently support. ACL is no longer recommended according to S3 security best practices. - The
Expires
option has been deprecated. - The
resumable
option has been removed fromconfig
as all upload tasks are resumable by default in v6. You can find thepause
,resume
, andcancel
functions onuploadData
's return value. See the v6 Storage documentation for details. - The
progressCallback
option on upload and download operations has been replaced byonProgress
in v6. The function input property namesloaded
andtotal
now map totransferredBytes
andtotalBytes
.
Input
V5
// AWSS3 (Default) Providerkey: stringobject: { ACL?: string; // removed in V6 CacheControl?: string; // removed in V6 ContentDisposition? string; ContentEncoding?: string; ContentMD5?: string; ContentType?: string; Expires?: Date; // removed in V6 Metadata?: Record<string, string>; ServerSideEncryption?: string; // removed in V6 SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKey?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6 SSEKMSKeyId?: string; // removed in V6 Bucket: string; Key: string; Body?: string | ReadableStream<any> | Blob | Uint8Array | Buffer; Tagging?: string; // removed in V6}// non-resumable configconfig?: { level?: 'public' | 'protected' | 'private'; provider?: string; // removed in V6 customPrefix?: { // removed in V6 private?: string; public?: string; protected?: string; }; track?: boolean; // removed in V6 progressCallback?: (progress: any) => any; serverSideEncryption?: string; // removed in V6 SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKey?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6 SSEKMSKeyId?: string; // removed in V6 acl?: string; // removed in V6 bucket?: string; cacheControl?: string; // removed in V6 contentDisposition?: string; contentEncoding?: string; contentType?: string; expires?: Date; metadata?: Record<string, string>; tagging?: string; // removed in V6 useAccelerateEndpoint?: boolean; resumable?: boolean; // Not required in v6: all put operations are resumable}// resumable configconfig?: { // All of the standard config above plus progressCallback?: (progress: { loaded: number; total: number; }) => any; // More specific than progressCallback above completeCallback?: (event: { key?: string }) => any; errorCallback?: (err: any) => any;}
V6
input: { path: string | ({identityId}: {identityId: string}) => string; data: string | Blob | ArrayBufferView | ArrayBuffer; options?: { useAccelerateEndpoint?: boolean; onProgress?: (event: { transferredBytes: number; totalBytes?: number; }) => void; contentDisposition?: string; contentEncoding?: string; contentType?: string; metadata?: Record<string, string>; };}
Output
V5
// ResumableStoragePutOutput { resume(): any; pause(): any; percent: number; isInProgress: boolean;}
// Non-resumableStoragePutOutput { key: string;};
V6
UploadDataOutput { cancel: (message?: string) => void; pause: () => void; resume: () => void; readonly state: | 'IN_PROGRESS' | 'PAUSED' | 'CANCELED' | 'SUCCESS' | 'ERROR'; result: Promise<Item>;}
Item { path: string; lastModified?: Date; size?: number; eTag?: string; metadata?: Record<string, string>; versionId?: string; contentType?: string;}
import { uploadData } from 'aws-amplify/storage';
const handleUpload = async (path: string, data: string | Blob) => { const operation = uploadData({ path, data });
const result = await operation.result;}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => { // Upload a file with access level `public` const result = await Storage.put(key, data, { level: 'public', });}
Monitor Upload Progress
The progressCallback
option has been replaced by onProgress
in v6. The function input property names loaded
and total
now map to transferredBytes
and totalBytes
.
import { uploadData } from 'aws-amplify/storage';
const handleUpload = async (path: string, data: string | Blob) => { const operation = uploadData({ path, data, options: { onProgress: ({ transferredBytes, totalBytes }) => { // Progress implementation } } });
const result = await operation.result;}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => { // Upload a file with access level `public` const result = await Storage.put(key, data, { level: 'public', progressCallback: ({ loaded, total }) => { // Progress implementation } });}
Storage.get
The Storage.get
API has been separated into two APIs in v6, getURL
and downloadData
. There are a few changes in the experience from v5 to v6 in addition to those mentioned above:
- Content options (
contentDisposition
,contentLanguage
,contentEncoding
, andcontentType
) are no longer supported in theget
API as these values are already provided in the uploaded file. - The following response keys inherited from AWS SDK in v5 have have been removed from the API output in v6:
- DeleteMarker
- AcceptRanges
- Expiration
- Restore
- ChecksumXXX
- MissingMeta
- VersionId
- ContentRange
- Expires
- WebsiteRedirectLocation
- BucketKeyEnabled
- StorageClass
- RequestCharged
- ReplicationStatus
- PartsCount
- TagCount
- ObjectLockMode
- ObjectLockRetainUntilDate
- ObjectLockLegalHoldStatus
- The
progressCallback
option on upload and download operations has been replaced byonProgress
in v6. The function input property namesloaded
andtotal
now map totransferredBytes
andtotalBytes
.
Input
V5
// Assumes default S3 Providerkey: stringoptions: { level?: 'public' | 'protected' | 'private'; provider?: 'AWSS3'; customPrefix?: { // removed in V6 private?: string; public?: string; protected?: string; }; track?: boolean; // removed in V6 download?: boolean; // removed in V6 expires?: number; identityId?: string; progressCallback?: (progress: any) => any; cacheControl?: string; // removed in V6 contentDisposition?: string; // removed in V6 contentEncoding?: string; // removed in V6 contentLanguage?: string; // removed in V6 contentType?: string; // removed in V6 SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKey?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6 validateObjectExistence?: boolean;};
V6
// getUrl{ path: string | ({identityId}: {identityId: string}) => string; options?: { useAccelerateEndpoint?: boolean; };}
// downloadDatainput: { path: string | ({identityId}: {identityId: string}) => string; options?: { useAccelerateEndpoint?: boolean; onProgress?: (event: { transferredBytes: number; totalBytes?: number; }) => void; bytesRange?: { start: number; end: number; }; };}
Output
V5
// download: falsestring // presigned url
// download: trueS3ProviderGetOutput { Body?: Blob; DeleteMarker?: boolean; // removed in V6 AcceptRanges?: string; // removed in V6 Expiration?: string; // removed in V6 Restore?: string; // removed in V6 LastModified?: Date; ContentLength?: number; ETag?: string; ChecksumCRC32?: string; // removed in V6 ChecksumCRC32C?: string; // removed in V6 ChecksumSHA1?: string; // removed in V6 ChecksumSHA256?: string; // removed in V6 MissingMeta?: number; // removed in V6 VersionId?: string; // removed in V6 CacheControl?: string; // removed in V6 ContentDisposition?: string; // removed in V6 ContentEncoding?: string; // removed in V6 ContentLanguage?: string; // removed in V6 ContentRange?: string; // removed in V6 ContentType?: string; Expires?: Date; // removed in V6 WebsiteRedirectLocation?: string; // removed in V6 ServerSideEncryption?: ServerSideEncryption | string; // removed in V6 Metadata?: Record<string, string>; SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6 SSEKMSKeyId?: string; // removed in V6 BucketKeyEnabled?: boolean; // removed in V6 StorageClass?: StorageClass | string; // removed in V6 RequestCharged?: RequestCharged | string; // removed in V6 ReplicationStatus?: ReplicationStatus | string; // removed in V6 PartsCount?: number; // removed in V6 TagCount?: number; // removed in V6 ObjectLockMode?: ObjectLockMode | string; // removed in V6 ObjectLockRetainUntilDate?: Date; // removed in V6 ObjectLockLegalHoldStatus?: ObjectLockLegalHoldStatus | string; // removed in V6 $metadata: { // removed in V6 httpStatusCode?: number; requestId?: string }}
V6
// getUrlGetUrlOutput { url: URL; expiresAt: Date;}
// downloadDataDownloadDataOutput { cancel: (message?: string) => void; readonly state: TransferTaskState; result: Promise<StorageDownloadDataOutput<Item>>;}
StorageDownloadDataOutput<Item> { path: string; lastModified?: Date; size?: number; eTag?: string; metadata?: Record<string, string>; versionId?: string; contentType?: string; body: { blob: () => Promise<Blob>; json: () => Promise<any>; text: () => Promise<string>; };}
Get URL Only (Default)
import { getUrl } from 'aws-amplify/storage';
const handleGetUrl = async (path: string) => { const url = await getUrl({ path, options: { validateObjectExistence: true }, });}
import { Storage } from 'aws-amplify';
const handleGetUrl = async (key: string) => { const url = await Storage.get(key, { validateObjectExistence: true });}
Download File Content to Memory
import { downloadData } from 'aws-amplify/storage';
const handleDownload = async (path: string) => { const { body, eTag } = await downloadData({ path }).result;}
import { Storage } from 'aws-amplify';
const handleDownload = async (key: string) => { const { Body, ETag } = await Storage.get(key, { download: true });}
Monitor Download Progress
The progressCallback
option has been replaced by onProgress
in v6. The function input property names loaded
and total
now map to transferredBytes
and totalBytes
.
import { downloadData } from 'aws-amplify/storage';
const handleDownload = async (path: string) => { const { body, eTag } = await downloadData({ path, options: { onProgress: ({ transferredBytes, totalBytes }) => { // Progress implementation } } }).result;}
import { Storage } from 'aws-amplify';
const handleDownload = async (key: string) => { const { Body, ETag } = await Storage.get(key, { download: true, progressCallback: ({ loaded, total }) => { // Progress implementation } });}
Storage.copy
In addition to the changes mentioned above, the copy
API in v6 no longer supports content options (contentDisposition
, contentLanguage
, and contentType
) as these values are already provided by the existing file.
Input
V5
// Assumes default S3 Providersrc: { key: string; level?: string; identityId?: string;}dest: { level?: string; key: string;}config?: { provider?: 'AWSS3'; customPrefix?: { // removed in V6 public?: string; protected?: string; private?: string; }; track?: boolean; // removed in V6 bucket?: string; cacheControl?: string; // removed in V6 contentDisposition?: string; // removed in V6 contentLanguage?: string; // removed in V6 contentType?: string; // removed in V6 expires?: Date; tagging?: string; // removed in V6 acl?: | 'private' | 'authenticated-read' | 'aws-exec-read' | 'bucket-owner-full-control' | 'bucket-owner-read' | 'public-read' | 'public-read-write' | string; metadata?: Record<string, string>; serverSideEncryption?: 'AES256' | 'aws:kms'; SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKey?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6 SSEKMSKeyId?: string; // removed in V6}
V6
input: { source: { path: string | ({identityId}: {identityId: string}) => string; }; destination: { path: string | ({identityId}: {identityId: string}) => string; };}
Output
V5
S3ProviderCopyOutput { key: string;}
V6
CopyOutput { path: string;}
import { copy } from 'aws-amplify/storage';
const handleCopy = async (sourcePath: string, destinationPath: string) => { const { path } = await copy({ source: { path: sourcePath }, destination: { path: destinationPath }, });};
import { Storage } from 'aws-amplify';
const handleCopy = async (sourceKey: string, destinationKey: string) => { const { key } = await Storage.copy( src: { key: sourceKey }, dest: { key: destinationKey } )};
Storage.remove
In addition to the changes mentioned above, the output of the remove
API in v6 now only returns the path
of the removed file. DeleteMarker
, VersionId
, RequestCharged
, and $metadata
have been removed from the output.
Input
V5
// Assumes default S3 Providerkey: string config?: { level?: 'public' | 'protected' | 'private'; provider?: 'AWSS3'; customPrefix?: { // removed in V6 public?: string; protected?: string; private?: string; }; track?: boolean; // removed in V6 bucket?: string;}
V6
input: { path: string | ({identityId}: {identityId: string}) => string; options?: { useAccelerateEndpoint?: boolean; };}
Output
V5
DeleteMarker?: boolean; // removed in V6VersionId?: string; // removed in V6RequestCharged?: 'requester' | string; // removed in V6$metadata: { // removed in V6 httpStatusCode?: number; requestId?: string; extendedRequestId?: string; cfId?: string; attempts?: number; totalRetryDelay?: number;};
V6
RemoveOutput { path: string;}
import { remove } from 'aws-amplify/storage';
const handleRemove = async (path: string) => { await remove({ path, });}
import { Storage } from 'aws-amplify';
const handleRemove = async (key: string, accessLevel: string) => { await Storage.remove(key, { // accessLevel is required if other than 'public' level: accessLevel });}
Storage.list
There are a few changes in the list
API from v5 to v6 in addition to those mentioned above:
- The
hasNextToken
output property has been removed. You can check thatnextToken
is defined to check if there are more items to retrieve.
Input
V5
// Assumes default S3 Providerpath: string,config?: { level?: 'public' | 'protected' | 'private'; provider?: 'AWSS3'; customPrefix?: { // removed in V6 private?: string; public?: string; protected?: string; }; track?: boolean; // removed in V6 bucket?: string; pageSize?: number | 'ALL'; identityId?: string; nextToken?: string;}
V6
// Paginated Listinput { path: string | ({identityId}: {identityId: string}) => string; options?: { listAll?: false; pageSize?: number; nextToken?: string; useAccelerateEndpoint?: boolean; };}
// List Allinput { path: string | ({identityId}: {identityId: string}) => string; options?: { listAll: true; useAccelerateEndpoint?: boolean; };}
Output
V5
StorageListOutput { results: S3ProviderListOutputItem[]; nextToken?: string; hasNextToken: boolean; // removed in V6};
S3ProviderListOutputItem { key: string; eTag: string; lastModified: Date; size: number;}
V6
// Paginated ListListPaginateOutput { items: ListOutputItem[]; nextToken?: string;}
// List AllListAllOutput { items: ListOutputItem[];}
ListOutputItem { path: string; lastModified?: Date; size?: number; eTag?: string;}
Paginated List (Default)
import { list } from 'aws-amplify/storage';
const PAGE_SIZE = 20;let nextToken = undefined;let hasNextPage = true;
const loadNextPage = async (path: string) => { if (hasNextPage) { let response = await list({ path, options: { pageSize: PAGE_SIZE, nextToken: nextToken } }); if (response.nextToken) { nextToken = response.nextToken; } else { nextToken = undefined; hasNextPage = false; } // render list items from response.results }};
import { Storage } from 'aws-amplify';
const PAGE_SIZE = 20;let nextToken = undefined;let hasNextPage = true;
const loadNextPage = async () => { if (hasNextPage) { let response = await Storage.list('', { pageSize: PAGE_SIZE, nextToken: nextToken }); if (response.hasNextToken) { nextToken = response.nextToken; } else { nextToken = undefined; hasNextPage = false; } // render list items from response.results }};
All Files
import { list } from 'aws-amplify/storage';
const handleListAll = async ( path: string) => { const { items } = list({ path, options: { listAll: true, } });}
import { Storage } from 'aws-amplify';
const handleListAll = async ( prefix: string, accessLevel: string, id: string) => { const { results } = Storage.list(prefix, { pageSize: 'ALL', // level and identityId not required if level is 'public' level: accessLevel, identityId: id });}
Storage.getProperties
In addition to the changes mentioned above, the getProperties
API in v6 also adds path
to the result.
Input
V5
key: string config?: { level?: 'public' | 'protected' | 'private'; provider?: string; // removed in V6 customPrefix?: { // removed in V6 private?: string; public?: string; protected?: string; }; track?: boolean; // removed in V6 SSECustomerAlgorithm?: string; // removed in V6 SSECustomerKey?: string; // removed in V6 SSECustomerKeyMD5?: string; // removed in V6}
V6
input: { path: string | ({identityId}: {identityId: string}) => string; options?: { useAccelerateEndpoint?: boolean; };}
Output
V5
StorageGetPropertiesOutput { contentType: string; contentLength: number; eTag: string; lastModified: Date; metadata: Record<string, string>;};
V6
GetPropertiesOutput { path: string; lastModified?: Date; size?: number'; eTag?: string'; metadata?: Record<string, string>'; versionId?: string; contentType?: string;}
import { getProperties } from 'aws-amplify/storage';
const handleGetProperties = async ( path: string,) => { const result = await getProperties({ path });}
import { Storage } from 'aws-amplify';
const handleGetProperties = async ( key: string, accessLevel: string) => { const result = await Storage.getProperties(key, { // level not required if level is 'public' level: accessLevel, });}
Storage.cancel
The process for cancelling a request has changed in v6. In v5, you have to maintain the promise or UploadTask reference returned from a Storage API call, and supply it as input to the Storage.cancel
API. In v6, cancel
is a function returned with the result of a Storage operation. To cancel an operation, you will call operation.cancel()
.
import { downloadData } from 'aws-amplify/storage';
const operation = downloadData({ path: "photos/1.jpg"});
operation.response.then(result => { // GET operation completed successfully}).catch(error => { // If the error is because the request was cancelled you can confirm here. if(isCancelError(error)) { // 'my message for cancellation' console.log(error.message); }})
// To cancel the above requestoperation.cancel('my message for cancellation');
import { Storage } from 'aws-amplify';
const operation = Storage.get(key, options);
operation.then(result => { // GET operation completed successfully}).catch(error => { // If the error is because the request was cancelled you can confirm here. if(Storage.isCancel(error)) { // 'my message for cancellation' console.log(error.message); }});
// To cancel the above requestStorage.cancel(operation, 'my message for cancellation');
Using a Custom Prefix (Deprecated)
If you would like to upload or download items with a customPrefix
, you can configure a prefix resolver in the second parameter of Amplify.configure
, libraryOptions
. Keep in mind that you will need to resolve each accessLevel
in the custom prefixResolver
. The example below shows the default for protected
and private
access levels and a customPrefix for guest
.
import { uploadData } from 'aws-amplify/storage';import { Amplify } from 'aws-amplify';import amplifyconfig from './amplifyconfiguration.json';
const libraryOptions = { Storage: { S3: { prefixResolver: async ({ accessLevel, targetIdentityId }) => { if (accessLevel === 'guest') { return 'publicPrefix/'; } else if (accessLevel === 'protected') { return `protected/${targetIdentityId}/`; } else { return `private/${targetIdentityId}/`; } } } }};
Amplify.configure(amplifyConfig, libraryOptions);
const handleUpload = async (key: string, data: string | Blob) => { // Upload a file with access level `guest` as the equivalent of `public` in v5 const operation = uploadData({ key, data, options: { accessLevel: 'guest' } });
const result = await operation.result;}
import { Storage } from 'aws-amplify';
const handleUpload = async (key: string, data: string | Blob) => { // Upload a file with access level `public` const result = await Storage.put(key, data, { level: 'public', customPrefix: { public: 'publicPrefix' } });}
Tracking storage events
The track
option has been removed from Storage and its APIs in v6. Previously, in v5, enabling this option would automatically send analytics events to Pinpoint following the success or failure of Storage API calls. The analytic events sent were defined by Amplify and therefore not configurable. In v6, this option has been removed but you can continue to track Storage API results in a more predictable and configurable way using the Analytics category.
The table below lists the analytics events previously associated to v5 APIs and the corresponding v6 APIs they should now be associated with instead.
Analytics event | V5 API | V6 API |
---|---|---|
'copy' | copy | copy |
'download' | get (download: true ) | downloadData |
'getSignedUrl' | get | getUrl |
'getProperties' | getProperties | getProperties |
'upload' | put | uploadData |
'delete' | remove | remove |
'list' | list | list |
import { list } from 'aws-amplify/storage';import { record } from 'aws-amplify/analytics';
const path = 'public/photos/';
try { const result = await list({ path }); record({ name: 'list', attributes: { result: 'success' }, }); // Do something with the result} catch (error) { record({ name: 'list', attributes: { result: 'failure' }, }); // Do something with the error};
import { Storage } from 'aws-amplify';
const prefix = 'photos/';
Storage.configure({ track: true });
try { const result = Storage.list(prefix); // Do something with the result} catch (error) { // Do something with the error}