@@ -32,6 +32,7 @@ import { randomUUID } from 'crypto'
3232import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
3333import axios from 'axios'
3434import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
35+ import { getInvalidObjectName , getUnicodeObjectName } from './common'
3536
3637const { s3ProtocolAccessKeySecret, s3ProtocolAccessKeyId, storageS3Region } = getConfig ( )
3738
@@ -1402,5 +1403,150 @@ describe('S3 Protocol', () => {
14021403 expect ( resp . ok ) . toBeTruthy ( )
14031404 } )
14041405 } )
1406+
1407+ describe ( 'Object key names with Unicode characters' , ( ) => {
1408+ it ( 'can be used with MultipartUpload commands' , async ( ) => {
1409+ const bucketName = await createBucket ( client )
1410+ const objectName = getUnicodeObjectName ( )
1411+ const createMultiPartUpload = new CreateMultipartUploadCommand ( {
1412+ Bucket : bucketName ,
1413+ Key : objectName ,
1414+ ContentType : 'image/jpg' ,
1415+ CacheControl : 'max-age=2000' ,
1416+ } )
1417+ const createMultipartResp = await client . send ( createMultiPartUpload )
1418+ expect ( createMultipartResp . UploadId ) . toBeTruthy ( )
1419+ const uploadId = createMultipartResp . UploadId
1420+
1421+ const listMultipartUploads = new ListMultipartUploadsCommand ( {
1422+ Bucket : bucketName ,
1423+ } )
1424+ const listMultipartResp = await client . send ( listMultipartUploads )
1425+ expect ( listMultipartResp . Uploads ?. length ) . toBe ( 1 )
1426+ expect ( listMultipartResp . Uploads ?. [ 0 ] . Key ) . toBe ( objectName )
1427+
1428+ const data = Buffer . alloc ( 1024 * 1024 * 2 )
1429+ const uploadPart = new UploadPartCommand ( {
1430+ Bucket : bucketName ,
1431+ Key : objectName ,
1432+ ContentLength : data . length ,
1433+ UploadId : uploadId ,
1434+ Body : data ,
1435+ PartNumber : 1 ,
1436+ } )
1437+ const uploadPartResp = await client . send ( uploadPart )
1438+ expect ( uploadPartResp . ETag ) . toBeTruthy ( )
1439+
1440+ const listParts = new ListPartsCommand ( {
1441+ Bucket : bucketName ,
1442+ Key : objectName ,
1443+ UploadId : uploadId ,
1444+ } )
1445+ const listPartsResp = await client . send ( listParts )
1446+ expect ( listPartsResp . Parts ?. length ) . toBe ( 1 )
1447+
1448+ const completeMultiPartUpload = new CompleteMultipartUploadCommand ( {
1449+ Bucket : bucketName ,
1450+ Key : objectName ,
1451+ UploadId : uploadId ,
1452+ MultipartUpload : {
1453+ Parts : [
1454+ {
1455+ PartNumber : 1 ,
1456+ ETag : uploadPartResp . ETag ,
1457+ } ,
1458+ ] ,
1459+ } ,
1460+ } )
1461+ const completeMultipartResp = await client . send ( completeMultiPartUpload )
1462+ expect ( completeMultipartResp . $metadata . httpStatusCode ) . toBe ( 200 )
1463+ expect ( completeMultipartResp . Key ) . toEqual ( objectName )
1464+ } )
1465+
1466+ it ( 'can be used with Put, List, and Delete Object commands' , async ( ) => {
1467+ const bucketName = await createBucket ( client )
1468+ const objectName = getUnicodeObjectName ( )
1469+ const putObject = new PutObjectCommand ( {
1470+ Bucket : bucketName ,
1471+ Key : objectName ,
1472+ Body : Buffer . alloc ( 1024 * 1024 * 1 ) ,
1473+ } )
1474+ const putObjectResp = await client . send ( putObject )
1475+ expect ( putObjectResp . $metadata . httpStatusCode ) . toEqual ( 200 )
1476+
1477+ const listObjects = new ListObjectsCommand ( {
1478+ Bucket : bucketName ,
1479+ } )
1480+ const listObjectsResp = await client . send ( listObjects )
1481+ expect ( listObjectsResp . Contents ?. length ) . toBe ( 1 )
1482+ expect ( listObjectsResp . Contents ?. [ 0 ] . Key ) . toBe ( objectName )
1483+
1484+ const listObjectsV2 = new ListObjectsV2Command ( {
1485+ Bucket : bucketName ,
1486+ } )
1487+ const listObjectsV2Resp = await client . send ( listObjectsV2 )
1488+ expect ( listObjectsV2Resp . Contents ?. length ) . toBe ( 1 )
1489+ expect ( listObjectsV2Resp . Contents ?. [ 0 ] . Key ) . toBe ( objectName )
1490+
1491+ const getObject = new GetObjectCommand ( {
1492+ Bucket : bucketName ,
1493+ Key : objectName ,
1494+ } )
1495+ const getObjectResp = await client . send ( getObject )
1496+ const getObjectRespData = await getObjectResp . Body ?. transformToByteArray ( )
1497+ expect ( getObjectRespData ) . toBeTruthy ( )
1498+ expect ( getObjectResp . ETag ) . toBeTruthy ( )
1499+
1500+ const deleteObjects = new DeleteObjectsCommand ( {
1501+ Bucket : bucketName ,
1502+ Delete : {
1503+ Objects : [
1504+ {
1505+ Key : objectName ,
1506+ } ,
1507+ ] ,
1508+ } ,
1509+ } )
1510+ const deleteObjectsResp = await client . send ( deleteObjects )
1511+ expect ( deleteObjectsResp . Errors ) . toBeFalsy ( )
1512+ expect ( deleteObjectsResp . Deleted ) . toEqual ( [
1513+ {
1514+ Key : objectName ,
1515+ } ,
1516+ ] )
1517+
1518+ const getObjectDeleted = new GetObjectCommand ( {
1519+ Bucket : bucketName ,
1520+ Key : objectName ,
1521+ } )
1522+ try {
1523+ await client . send ( getObjectDeleted )
1524+ throw new Error ( 'Should not reach here' )
1525+ } catch ( e ) {
1526+ expect ( ( e as S3ServiceException ) . $metadata . httpStatusCode ) . toEqual ( 404 )
1527+ }
1528+ } )
1529+
1530+ it ( 'should not upload if the name contains invalid characters' , async ( ) => {
1531+ const bucketName = await createBucket ( client )
1532+ const invalidObjectName = getInvalidObjectName ( )
1533+ try {
1534+ const putObject = new PutObjectCommand ( {
1535+ Bucket : bucketName ,
1536+ Key : invalidObjectName ,
1537+ Body : Buffer . alloc ( 1024 * 1024 * 1 ) ,
1538+ } )
1539+ await client . send ( putObject )
1540+ throw new Error ( 'Should not reach here' )
1541+ } catch ( e ) {
1542+ expect ( ( e as Error ) . message ) . not . toBe ( 'Should not reach here' )
1543+ expect ( ( e as S3ServiceException ) . $metadata . httpStatusCode ) . toEqual ( 400 )
1544+ expect ( ( e as S3ServiceException ) . name ) . toEqual ( 'InvalidKey' )
1545+ expect ( ( e as S3ServiceException ) . message ) . toEqual (
1546+ `Invalid key: ${ encodeURIComponent ( invalidObjectName ) } `
1547+ )
1548+ }
1549+ } )
1550+ } )
14051551 } )
14061552} )
0 commit comments