Browse Source

Merge pull request #176 from aliyun/preview_v1.9.7

Preview v1.9.7
wangtaowei 6 years ago
parent
commit
61282e6241
15 changed files with 908 additions and 55 deletions
  1. 15 3
      .travis.yml
  2. 75 0
      oss/bucket.go
  3. 226 0
      oss/bucket_test.go
  4. 92 0
      oss/client.go
  5. 178 0
      oss/client_test.go
  6. 10 1
      oss/conn.go
  7. 22 1
      oss/const.go
  8. 23 1
      oss/option.go
  9. 54 8
      oss/type.go
  10. 1 1
      oss/upload.go
  11. 41 24
      sample.go
  12. 51 0
      sample/bucket_encryption.go
  13. 26 14
      sample/bucket_lifecycle.go
  14. 19 2
      sample/copy_object.go
  15. 75 0
      sample/object_tagging.go

+ 15 - 3
.travis.yml

@@ -10,13 +10,16 @@ install:
 - go get gopkg.in/check.v1
 - go get github.com/satori/go.uuid
 - go get github.com/baiyubin/aliyun-sts-go-sdk/sts
-
-- if [[ $TRAVIS_GO_VERSION = '1.7' || $TRAVIS_GO_VERSION > '1.7' ]]; then go get golang.org/x/time/rate ; fi
- 
+- if [[ $TRAVIS_GO_VERSION = '1.7' || $TRAVIS_GO_VERSION > '1.7' ]]; then go get golang.org/x/time/rate
+  ; fi
 script:
+- if [[ ! -n "$OSS_TEST_ACCESS_KEY_ID" ]]; then exit 0
+  ; fi
+  
 - cd oss
 - travis_wait 30 go test -v -covermode=count -coverprofile=coverage.out -timeout=30m
 - "$HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci"
+
 env:
   global:
   - secure: ZCL5egxJmZA+o1ujsaJe//AIh1ag/exSUJ2FzoKPF3O9c84hMc2k5EYO2rGzTNn1ML6M89Mo5hAvHQhyJEHjRuMtjc1QrfxAaA3mqm4scGXIXesPMqYGuvuPSh++6/fkAwaVBAhrk5GaDG1/FuxHE5zusGx3SvGegnCwO7n/2YCfXco6DWgVCdrz4p1EpPkAM3JIdHFUzsDWiimVuiNAvJmAT8+IeOPTT+WgusCJj4ORS3X3LddTjttBP+hRrp/pGSoNqPMzfysWybtaL2SJ8URtvsxW0Mo5BwocHAxAhPP+M2OscQbDzthSAezCLngYvrfBplfIyWlahlgzNz/FjXz5pQwWdYVNoibyxLLMOH685n75LNONN/xVO/GFmVPx7DMGapkN5NzIWS62D4v8QrRkwtms42OUkyEUHjDh8Evui3K2MNJVXA3TI9zOAR+C0krD7OEyS37qrppodhRxJSqFUlgXnk//wLldMC7vleDd7L2UQSWjqyBHqFOgsVaiLU2KRTY3zvv7ke+dqb5VF31mH6qAr8lJTR9un8M1att0VwCEKxoIRT4cKJCpEtZd8ovXOVt1uE695ThVXE9I5e00GXdTzqXOuv6zT4hv/dgmbz9JN9MYeCwmokEoIUmJKNYERa/bNVVefdnJt7h+dm+KpyPAS+XvPLzjbnWdYNA=
@@ -28,3 +31,12 @@ env:
   - secure: NMVS9EU+ahQXGiyTCHyZ44rf+8b3me3UXD1DozMm04lCvnWoBqJE4aXBGQsDAWuOL4NTTm0SaVu6sBY6ZTXOYYF59mwEbxt4qpmVjZ+vBrtMbMiqoxv145blquR9JKedkdP6IGSd7VSQwSba71f/RVv5VeGvxUSEhCwA04kKxToOPwmnORmT6qwb7PkPCMNHxz4VpsUIsKx8jRrY6Gmp6FvQJBHfKEHnDQohB1ReIYEYi39ijLvpbCZqrB5u1N9oF6WlpBiNIX3kQizn7ftUyewJgoZMnfpW/Lta6e91yzFInWg75bZdW3faa30Qy0yw0zlQIPLs89c8A/XH1fGVECH9At9VNmdYrb0fD9aWnH7zdX6Im+Bw7Ptph4x6tB7zPeFoZR5cVZT7L06/HbnW7NeQk4tg/N4I1tOaO7AQl+ofhCzesZ56bSxETiNFn9QiNwWFTzjlkG7jxN1iAAkdYsZEQHwtEK63R//NJtXpbbtNA831QqgDqBK+IxyKeLhmxmu17dWcUw9tm4jlZ7d6nPB9bzJcVM6K2uRJyW07SlBqd65WJTXPV1PFww8zh+chAC4ZkLDhupn+7ZSG2ylLYGgepmABoC/CXHkXEsNzdQ8wPX/pDIz2WNmwEXyC/Nv+WNpFS/tWIAryIPOLMuETIgbaOLbD5vZDSKxDZVGDvPE=
   - secure: cNr4PiK6ZZADoRMacL4lvdMYWgM9H4lKN3u+nfMA/MrVrnSjeRonkO7RjMJWs9HicPanEks10R1w/T/6nWyFQV2CPkEBSNSLf3DAD+dlRekKfWogGXzYnvqeiki1HzsIPYTImiD5BtPn6SbJmO/ErJt3zuogspyBws/X7XfZ+u8FIpPsYEmvHslT2jARuput0yNfldUgxxyI0IvgkuCEcCTFwePspjbn6zR6Df67e+r5ibFqkdPYdXkQVpvfA90RPpfBUuuaEO7kkFlKbPK+Nl/jbUnjcfbe8zJRpSb8j/S2USAiBUjFsqsdvFqZ9WumjXJLrrNFt/UgIXaMyG3Y8xJl9kzCcx36wcNoaP2mx2qucYTdC0ey49g0uywvOVDdykmctQRF7uYQS+UkMqs5jRLgAjQ1/wJISrvtcpaK/4DyhLBUFrOf9chep2hzWBFaWPto1IUpWu9Sjtz5rtjsAm5LR7zvIxcorvRall5kRokAspHb9+TaQKSDOpYNF+PB77cb9H0OLZBLVPGo0WJHq5dv0NVZSH9JVq4AiJDmvMWI6weBis+tLbECMwbeTezo6hDOuII7VZt/KcHgzlt3KCDwv8krQq71q7ySDt7SxrvLeDjeiEFkjwA0lN7Cin1yqjON83LsIsWkKxbf+xNJRngSE4bPn95j3pHELdo3uqY=
   - secure: iDkNjibPknbawHN+jobw1AEqhQDhqFvGPBUX7FkxlrIanNR71Tj8CPAFtDpJbYaBMdPt4IzOmD2JVo9w7E1TfNX4DsOpkb2MbOr55TxfXQ/+y7TBdJ9B/62BvhFWk8Hvq8TWVPTCgNIaVXNfqBAj6WQSX3AbUVHWSOM9Aey5joBZuhdWXSmKo46lLOradacDDPZYjrLEsMTS2CotzduKQ4C8dGCVcMEbBnS3O2WRj3oR0wiiP3X0jbnxJkOV2MCoadZxSu5B+gaaJ+Yv7EKT0vy6ASp6LYrWkjY0eKbTqy8NtCvCFlliND/iaq4LEv838hQfO/o0WeB2b7/2MH2EW1v8XLacV12ak5wJgb7b+L6fG+lMKMta5Re+vjdRYgoU5EVeWQNxrnX1chEdzFXb/q2+5DVp43qH5i1Tu4FR/kSBobQeSAbT7HTkWAVz3kg8HmubYZ3P0eXToZA/DlX0dphWxO9ShR3H+XTJhh3tSqzxMZxxhGqPcN4DPSfOTnJQ0v0NPz016lulCr9SuIOSM3f7HpeGXo5SeQbrY3yCnBG8Qxpx2kYaZZlT4J6fx3iFl77SY/lQu6H/Y8ZtufWEogPSkGEh+NLLWuwwBQFC3vH8l3J26vcqHZR3N9+GyqX13CSqWEUysMF4nBOi52ckhwJRF8hAeX+DIqxoLfjUkDc=
+  - secure: iZmWFdPBt9JCDJqWGeX0gznM22ulUrz15Z7YnDfWNhniMs759YWgIdt3MGr2blMmo5MpDZiticaBcE9aX3p7aniPZkbXcebswz5DwCKVRUf1dhtrzwbnDebSwKrlYG8SuGgMkKH/7F+sEkHtTy+SFDkFwEAwcGjPdPntEX15U6KPrlpD5AOvq/d83xNV6QYleJmkRjsTiUjgGzbYfm6Lu/bzBsckYjwWjcz5rxkeSIQHV8/nnw7xs0figTVcb0K5p7Rpvhg3TCAlSJcljg9IDOEuvILQGNDpdgzXajTTNOHYm9nOBrNQhTn35c8DRDl0uQFe+c6PBvBNhSvSKZ2t9REjok70gR/IKJNTuecS/rppYG8wfjlGg1axoJMZJG1OwOJxWiMHwa9WM/diJ2e5D/WTbQ1T9DLRp8Aco3LnWSfZ+tZH+4tAS9FE4oXEPLlHtRyRIV+MzPK3HinIXYdwGH7pDaAQDnfH562br3tl3CovjAPSrIlDgJh92qatLk+1fBQT/7paMNdelAWLjcujRRI9MWsKzi/aEUVbTfUOSCJH/vWQRJR9or2saOobHE+G4f2+k77r21YRztrZaJF6aiob/qf3altKW5rQRdj/7PgNtG3N/bwFOgRjpOQ8dgNn0J+xK4PQTtSVH7kD4aIcJma23erS+K29dcY/wk6fuBM=
+  - secure: IkTwVHWb8yGufpyBaQDL1WpTywnMb5FhhtXgP8ycxK9MIuKSfgxPlQm6Oel/8Xjy2HhfKTUX0TOwQ4yAuZtRuU/T5NdjGgBYdLjGWwd/Wgkxt7JkpkFd8lvMrh2moAgvf/Qfk0P73OdAyn2y2CZhm7F7GI3lD/K4NmbFnR5AkDGDBB7J81zc1eTGjMfYRELHMFLJTg8hKr64SljXuRBmyxAVImaBROXzml2pP3UywWKNsw0eEOaeJaVJFkStBh89IOYaznckdstdbTuMw9+hZywBTcQHqxaaI5stdtyKgJcyEcjdJOqNGwCf1Hqd18SH7aCfXsUUrwx62ZSheiQRapOz+XNMiKC+1nimNvaYIFEuPZUGjYfi2/c27Z6paBI9ylGpoUXSvCH2rmaCYUIJBRXVmqzvhi0am5FKBh9NovkKUE24B8rMhm1xV82HQLTCho6MnemXFfji2AXaJzYAa6lU31b8BAzQfy+Nn9lh8I/MMjd15Y+8nHFjKqeKZIBi5qO4B8AGNYwCBFhrIOF6uHGubOuvyKv8DSBxcaDhCoyOLa5QLT4vQzQCX17xUAFomXkX9mHbJA8kTxMaFOQPeVCHkfjZxuui57vbPt4CafGzbymi/SE03oNUheQbuHdqy+jkjj7XU6d4FdNYHxp4rvhtvjYrPJQ5gr6A3sdurd4=
+  - secure: WNjlMEt8xGRc6blhZF9HkyWEQukDzEwbk/hIljEz+K00W+ltDxPqFZ4fsgnjCPLS0fTL1zgtg3Rji6Mi8hSQd9Z5Yh98fCHN/gBfGxziKB0BOlVQDAl8yaUchiHZ6biM+HC2AocbZXlaNcSOHJy7vQk981y48PNPFadZgJZWWF4eFMkAn+74sPMW53V9J4J2ot3AZ7+ABg2vHTbqdA1s09/6fjlTcKVSxm7eDkkoOYycxrD5k2lm3N0I3YvkWum3ZMcCi1bWk4WXQSgy8q0dy7QBya7+BnRWh/EO2HNFAO4l/tqdev7GC3/tSgTbepNV5GUHazvsrANzZGnWP5kpjjis+Z4GiB3TMNLN1c+KeH2kbBkxi0vyDP5rzzI/rORRp5a3SBDAPkz0u/CHB9sltriErp86v9w6YxDRx0QcFheEXbcsk+3Vajy3+v1ly2PgMfLXcfojCfc27ymUCCymXMHrTG8DeUMnfIyUoP2xDx59tZH8rRlbUQmtlLhBYxPojsw5I2YhnMv4ThHZ5RhGSbNQuZ2yHsdQeGcTqSZ+bVT6xHbQbP4nk6ZtLHC6eq/pAQjFFUkZhfQLH8fes/2fVbdiRjYeX4P8tGnc2NB/fw3C77klizMYIGNHDAK8pKbfov5Q1zeg5/QB7weOvv9xpizUAjQsW5+jwQ1tkdxC0iU=
+  - secure: kllkCVsDmsu5Z4ZBMFN98VS5LNoyy0z8mpkreV1sAfPH8mmy/q1ZJAGkXLoXL5vFGOTBumMsDs6a7S74d1dXyBkQGYiegs2G5wqIvWb/hsq4dTudEMPqwWnVPCFs/YmtxtzjX788sS7ivkZsHA2pXh1w6glEZykXHJZgWWGSL6XFofR5Gmvpwc8SfQBP/iBi1xb0EA9YWUqjfW43FAuRMp3QDKQU2gcRoAr1EuSIvmMmarnK9CEMJunK/RY94OW1AwtUcJbUVLnK/PvHBIazY18BTez/o45gATH37eMk0G3JKxMP8yfk+wipAAVyZbloWeTdBW8ik1r/B9NWSI4ZXi/PysXMZzSkrede3Y0TE0TqW3mVkdtdcq4yFXD/B21dMmeNoBLxQGi290p/KNfnJLIcYB04CU+Ll9RoXWnVJH6RhxCK6JGEuiYqJKm/UZkCW5dXMhtFmjK3XH3iBRC9kw0jYI7fcTgstLzYzKzgAXBP7LSiiwxDUFsvq480US+lECGdDIJYL646H1kHogi2Cr3BBgXHvMoBbUFyLVXTHidzi4Vk6PFaT8fMXcJIO/j46WUcP5nyEh+7qiJmgqtZx6AbVIuank5vnDSKg9X5fsiww4cT6Xcf+E9rxcpiLhcfBjsbkUvKiVVcCNQHOIZxYLwEwelghz+VlWrJCFVPwT0=
+  - secure: g0Gm0G5oj892VmJeX8s2MMNttIR1GW9DX8+DYR2qXs5C8pjT8A30dYxCFiv4a1QHIrPEAhIGIosgtWpHXt8JwCDNLbPaHs6Jp9EkCB6doqLqQ9eOrvdxM67fyp8fJVcd4IH5JenG6FoaE7ofnu4QKoLo5w96Cd2UthwxrSCSQXWniutX8/N7XUt2cE74ISqQbJyslPWJ2UvKYL16HHtB8EzRmxD+EZgSYU/vtlds39Knrk85ogK5h6PKDmne8qoIH1nTPs8vzAu5uzeG6zai5m1qYETYbkiyJPTDs6sdy+DkYLLDaE5nLkNyfC/sHQvbPQ/H/CAW1OTZvSPsjxDN4hksovLaVuiH1DTHTM2+4fZsEab6fhx1itPOVijskoBqMzmksvah0nogIaw/qKmCtXGNBk7I30xeSju0W6c55miYUEEczF3eUn9oxHUSUDhQNs8UuosW/MJph7WTS2ESvmIjU7R6QuQjKmxTK7kZTaLAPpSwj/f77F7RgLUu3v6tO3Db1+i1OxdHvbaIVDiCNPB2J1LNmN4LFQstzJepYRadEUxYiExQzNulmC/FpkyPGwajec06z1r5NvTcWSOgxcTU/5OY6ggRCAsFoZTH4VzAst5nOBLRRGT+WAXpewGLhRlGLhev45eGgL2ctNVZY/6MA0ZBwnRPreczO7f3Nno=
+  - secure: iJ9fPWg8j+q19cUNqvwAbULZTnmK25wFuF5uBCxaSDuM1qMtbiq3M2l1Gvl0xZWdnq12PLMsUZtfLYNO1r4SRaarMPtnq4pK68DnuH49vkjpwGSy5bLIrcPQBZvzWKqzyG1R8+eOpg43BmXdau8Wo7pOSDw/8Bra7rtvA8Kjua/7MXaiZP08L71DtHaHFOoYlvgGHmuscdvtV3v4rNPntGR54ClzyNgnrRz6DP/NwEUF2a6EF2uhhbbe5XQvCNDTW0sqgxirZ4pDFPgywuWQaPcqqSXcwF78wrA0nLSzmQ1PsW9g/rUkDPbXE3uZuwje8QntNsSa8doTLw6Fizj7ZD8DuyvD8z3ljrLvhym+I00r8k6BpD5vlVhasUEEix/3+7lW4cpbE+ofwVz0Ge96y3Ei/uXkSI4kEhwPVfsU9sCRX/Kupdx9fLCQzm7F/BhkfHUCd403MjpbAtoHN3ACZfzC2QQ5fm14mxtLyMSawsClqwO7vVmc7+5p4f13TTkiUtWllKghMw8zTqxAZLwTiPFRKG6Y7MkX9tQUDBb9auQm/+7+pBFEUHibwoPvsSRwYGqdlZV7OXIFYPgt+WgUt9ZYblzSfZ8YLcGPfVTlGbLHkvW2SgIGL1gvHyXC2xd7kd7yec3M7AutDCiYg2kSzANVuJqGQCZSNr/6/S3QQrY=
+  - secure: IM9VEzf9MrtQnzK3VSD+YxmZWycxTgB5YbINDwE0LsAwWUWucDq95+2ZZRjKLrip5uYvoMohC5X2lxKuLhW8uDN3M6MxondHcCjgBCh8hWP7JociXWMxSIWA5ctU0oCgobm6rKvbarDuO8Bx+NE4QDDAWgjy9oOywE7mVi1G2//LY+2zC+iwXlkLs7VRdo9wtKgIG+FvswiJHhvFbU6GYxOeh1xclLnjwoWf66zzzbIW9Z4bFY0Zun/3u1ySQJWfF03/6ZzlCi3JPvfXDQ35T6PbD/5BvMDPmiXyWGqKUpZfRNus8VkG1G8TWwvHsVct9M47sZqLDuyzWVcwx613i8uxDsEIp9pVy4IxVl2UxvDJH1CPnNdvEhS3NeD9XfkMKMJtShHu1iVzB+j668W5SXsiv/N+psa2SIs+wuDY7FSirRg2Uwk9mlsXS+lSgV9B8BEcEIPEngA40MZCrEzNUdI0iYxaaqB6GNWeHiszOWz94rMkXk+FF3Ic75O4RyCmfXJmR8wYQs+rH4j7JJGzwTHXpYpV7H5V0OtMVIh6EUEvu3Veu/lkT4rsop55OHmcBmpKv8gGllX1P+g7H0+g35wn6Hz+FaSlkPvAuuSudgHFznOtfCowYhDUVp0TxTkcraLhkiROD4Qidw8r/HWh5CzrkfM9LWEBvziWFM77X/M=
+  - secure: SfJARcGrIyoemZ1jUENyX272acqmDTqQjk0LBjuzWxR5gqWp0Bn5TJS6x7niD8qzVC/n2FwR7F9FG3ll0cmToBgb8vh9763aL+ciA8cfk8iIUdjjQHNkywdhiLLw1EiR4TuUGOB3pV03WxMXl5JJTopJHScHgvTHfyhHS4vnQdrU3j6PwFDvVLsECyFiTCflDUgZ6zHocbIT4fqVjoBxy+5R0t9vyb4eE3IE3Lqjnac5oYVXm4KCpGUYDdCL0W2OlwPYgaBtWGetoWj/v6Kg4cjUVbKJHGK1q2nKyrCGPRvoKTqw83Pkfw1zkSdC0kCozMKr/gYsYBpt5IFXHaON/OkjvgdLbVKrpAHoauQ5KkWcTg9ntuUNndtg5pMaitBmd/J2AaMIxEde8ZvgfDR3sZdv0umFzLXJeAu/tbLR6ozP/BwOFpj89Lw0jNyNxJjdkLHP/4M2W5Ka0W1s+F6xNTahTuXdV32laPGN8SSmBrjzXwFOLOQTCgwlbD+YbTqAXUs+DLq/zIT6ZTaEfOdas+HTsu6H+cQfsmv9JlMP7PoT/CbL2tYe8kA2tjmxsQwUlVTcE1nP5ni5kHX+yXuClTzLimY+BOqU9SLJBKv6umygFqt/9nXk0YU6+UqfeDFCVzrMN0vnzWkmB0SQ3ElDC6pbb6NE/2RgmIy1pAUMDzY=
+  - secure: NPgEmEHVxJrLtaQ35IJnmQSbEHMqzSJt1l0OHX+a6VzfBiAP9Z4Qv1Ita/SQZpA4roLK2YkGr6TALndXgiY1EXU3e7ebcPnCgaQ6mCSJpAKtXhSGmArvysEmmxKSJjMxc8aBlJAeeEQlngXb0/pqA//kMw5KC+pPo+8O3UUbBs0qY9Q4FX3/cU+chv04tk5cSVR2P9nhOqY7LYm+FXjwF4vfjGKF7mWEcQUzTS8E2dRsertl/4law5O9/0gEW/9MaqyylPxq+0pS38sXP25zYotgEL3L8t0fupi58loPioDndardxVl35SXaatPuOxEdHr3Q0Q3dKVjbrTEx8bP7NgLY4nh7r5hqcEtvlM0xCAsfiOmGe0Dd0BFqo7kxzK/Fl6oI4MoEfvAYR+1QyBy9zOAJmjoj1hu/NEDAtT9NPxXJKl4A/iJPIYIt3lrMw34ewTSLl16ERojx6CZfSqg58eyUndmmOPBB8b4rIX0fQj5/ShsoLbM/rmOmDpPXN0QGu2EHhtR2eCbpdan84VfPifi5LjPH42PjdUxpJ+rOHZJHLesJ5JwQLFLrnDf7Bg96iucyz1QMpNFoqN+q4YDARot2zdw+1ISjgPx3bhHu8ly8oT1UYNl5mMjv7zfNDvsmBycpQEBQWGvIioRfx6dM/EtZy2E/og5Ci0j9v19YI2E=

+ 75 - 0
oss/bucket.go

@@ -927,6 +927,81 @@ func (bucket Bucket) ProcessObject(objectKey string, process string) (ProcessObj
 	return out, err
 }
 
+//
+// PutObjectTagging add tagging to object
+//
+// objectKey  object key to add tagging
+// tagging    tagging to be added
+//
+// error        nil if success, otherwise error
+//
+func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging) error {
+	bs, err := xml.Marshal(tagging)
+	if err != nil {
+		return err
+	}
+
+	buffer := new(bytes.Buffer)
+	buffer.Write(bs)
+
+	params := map[string]interface{}{}
+	params["tagging"] = nil
+	resp, err := bucket.do("PUT", objectKey, params, nil, buffer, nil)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	return nil
+}
+
+//
+// GetObjectTagging get tagging of the object
+//
+// objectKey  object key to get tagging
+//
+// Tagging
+// error      nil if success, otherwise error
+//
+func (bucket Bucket) GetObjectTagging(objectKey string) (GetObjectTaggingResult, error) {
+	var out GetObjectTaggingResult
+	params := map[string]interface{}{}
+	params["tagging"] = nil
+
+	resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
+	if err != nil {
+		return out, err
+	}
+	defer resp.Body.Close()
+
+	err = xmlUnmarshal(resp.Body, &out)
+	return out, err
+}
+
+//
+// DeleteObjectTagging delete object taggging
+//
+// objectKey  object key to delete tagging
+//
+// error      nil if success, otherwise error
+//
+func (bucket Bucket) DeleteObjectTagging(objectKey string) error {
+	params := map[string]interface{}{}
+	params["tagging"] = nil
+
+	if objectKey == "" {
+		return fmt.Errorf("invalid argument: object name is empty")
+	}
+
+	resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
 // Private
 func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
 	data io.Reader, listener ProgressListener) (*Response, error) {

+ 226 - 0
oss/bucket_test.go

@@ -2778,3 +2778,229 @@ func (s *OssBucketSuite) TestUploadObjectWithWebpFormat(c *C) {
 	bucket.DeleteObject(objectName)
 	client.DeleteBucket(bucketName)
 }
+
+func (s *OssBucketSuite) TestPutObjectTagging(c *C) {
+	// put object with tagging
+	objectName := objectNamePrefix + randStr(8)
+	tag1 := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tag2 := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tagging := Tagging{
+		Tags: []Tag{tag1, tag2},
+	}
+	err := s.bucket.PutObject(objectName, strings.NewReader(randStr(1024)), SetTagging(tagging))
+	c.Assert(err, IsNil)
+
+	headers, err := s.bucket.GetObjectDetailedMeta(objectName)
+	taggingCount, err := strconv.Atoi(headers["X-Oss-Tagging-Count"][0])
+	c.Assert(err, IsNil)
+	c.Assert(taggingCount, Equals, 2)
+
+	// copy object with default option
+	destObjectName := objectNamePrefix + randStr(8)
+	_, err = s.bucket.CopyObject(objectName, destObjectName)
+	c.Assert(err, IsNil)
+	headers, err = s.bucket.GetObjectDetailedMeta(destObjectName)
+	taggingCount, err = strconv.Atoi(headers["X-Oss-Tagging-Count"][0])
+	c.Assert(err, IsNil)
+	c.Assert(taggingCount, Equals, 2)
+
+	// delete object tagging
+	err = s.bucket.DeleteObjectTagging(objectName)
+	c.Assert(err, IsNil)
+
+	// get object tagging again
+	taggingResult, err := s.bucket.GetObjectTagging(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(len(taggingResult.Tags), Equals, 0)
+
+	// put tagging
+	tag := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tagging.Tags = []Tag{tag}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, IsNil)
+
+	taggingResult, err = s.bucket.GetObjectTagging(objectName)
+	c.Assert(len(taggingResult.Tags), Equals, 1)
+	c.Assert(taggingResult.Tags[0].Key, Equals, tag.Key)
+	c.Assert(taggingResult.Tags[0].Value, Equals, tag.Value)
+
+	//put tagging, the length of the key exceeds 128
+	tag = Tag{
+		Key:   randStr(129),
+		Value: randStr(16),
+	}
+	tagging.Tags = []Tag{tag}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	//put tagging, the length of the value exceeds 256
+	tag = Tag{
+		Key:   randStr(8),
+		Value: randStr(257),
+	}
+	tagging.Tags = []Tag{tag}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	//put tagging, the lens of tags exceed 10
+	tagging.Tags = []Tag{}
+	for i := 0; i < 11; i++ {
+		tag = Tag{
+			Key:   randStr(8),
+			Value: randStr(16),
+		}
+		tagging.Tags = append(tagging.Tags, tag)
+	}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	//put tagging, invalid value of tag key
+	tag = Tag{
+		Key:   randStr(8) + "&",
+		Value: randStr(16),
+	}
+	tagging.Tags = []Tag{tag}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	//put tagging, invalid value of tag value
+	tag = Tag{
+		Key:   randStr(8),
+		Value: randStr(16) + "&",
+	}
+	tagging.Tags = []Tag{tag}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	//put tagging, repeated tag keys
+	tag1 = Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tag2 = Tag{
+		Key:   tag1.Key,
+		Value: randStr(16),
+	}
+	tagging.Tags = []Tag{tag1, tag2}
+	err = s.bucket.PutObjectTagging(objectName, tagging)
+	c.Assert(err, NotNil)
+
+	s.bucket.DeleteObject(destObjectName)
+	s.bucket.DeleteObject(objectName)
+}
+
+func (s *OssBucketSuite) TestGetObjectTagging(c *C) {
+	// get object which has 2 tags
+	objectName := objectNamePrefix + randStr(8)
+	tag1 := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tag2 := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+
+	taggingInfo := Tagging{
+		Tags: []Tag{tag1, tag2},
+	}
+
+	err := s.bucket.PutObject(objectName, strings.NewReader(randStr(1024)), SetTagging(taggingInfo))
+	c.Assert(err, IsNil)
+
+	tagging, err := s.bucket.GetObjectTagging(objectName)
+	c.Assert(len(tagging.Tags), Equals, 2)
+	if tagging.Tags[0].Key == tag1.Key {
+		c.Assert(tagging.Tags[0].Value, Equals, tag1.Value)
+		c.Assert(tagging.Tags[1].Key, Equals, tag2.Key)
+		c.Assert(tagging.Tags[1].Value, Equals, tag2.Value)
+	} else {
+		c.Assert(tagging.Tags[0].Key, Equals, tag2.Key)
+		c.Assert(tagging.Tags[0].Value, Equals, tag2.Value)
+		c.Assert(tagging.Tags[1].Key, Equals, tag1.Key)
+		c.Assert(tagging.Tags[1].Value, Equals, tag1.Value)
+	}
+
+	// get tagging of an object that is not exist
+	err = s.bucket.DeleteObject(objectName)
+	c.Assert(err, IsNil)
+	tagging, err = s.bucket.GetObjectTagging(objectName)
+	c.Assert(err, NotNil)
+	c.Assert(len(tagging.Tags), Equals, 0)
+
+	// get object which has no tag
+	objectName = objectNamePrefix + randStr(8)
+	err = s.bucket.PutObject(objectName, strings.NewReader(randStr(1024)))
+	c.Assert(err, IsNil)
+	tagging, err = s.bucket.GetObjectTagging(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(len(tagging.Tags), Equals, 0)
+
+	// copy object, with tagging option
+	destObjectName := objectName + "-dest"
+	tagging.Tags = []Tag{tag1, tag2}
+	_, err = s.bucket.CopyObject(objectName, destObjectName, SetTagging(taggingInfo))
+	c.Assert(err, IsNil)
+	tagging, err = s.bucket.GetObjectTagging(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(len(tagging.Tags), Equals, 0)
+
+	// copy object, with tagging option, the value of tagging directive is "REPLACE"
+	tagging.Tags = []Tag{tag1, tag2}
+	_, err = s.bucket.CopyObject(objectName, destObjectName, SetTagging(taggingInfo), TaggingDirective(TaggingReplace))
+	c.Assert(err, IsNil)
+	tagging, err = s.bucket.GetObjectTagging(destObjectName)
+	c.Assert(err, IsNil)
+	c.Assert(len(tagging.Tags), Equals, 2)
+	if tagging.Tags[0].Key == tag1.Key {
+		c.Assert(tagging.Tags[0].Value, Equals, tag1.Value)
+		c.Assert(tagging.Tags[1].Key, Equals, tag2.Key)
+		c.Assert(tagging.Tags[1].Value, Equals, tag2.Value)
+	} else {
+		c.Assert(tagging.Tags[0].Key, Equals, tag2.Key)
+		c.Assert(tagging.Tags[0].Value, Equals, tag2.Value)
+		c.Assert(tagging.Tags[1].Key, Equals, tag1.Key)
+		c.Assert(tagging.Tags[1].Value, Equals, tag1.Value)
+	}
+
+	s.bucket.DeleteObject(objectName)
+	s.bucket.DeleteObject(destObjectName)
+}
+
+func (s *OssBucketSuite) TestDeleteObjectTagging(c *C) {
+	// delete object tagging, the object is not exist
+	objectName := objectNamePrefix + randStr(8)
+	err := s.bucket.DeleteObjectTagging(objectName)
+	c.Assert(err, NotNil)
+
+	// delete object tagging
+	tag := Tag{
+		Key:   randStr(8),
+		Value: randStr(16),
+	}
+	tagging := Tagging{
+		Tags: []Tag{tag},
+	}
+	err = s.bucket.PutObject(objectName, strings.NewReader(randStr(1024)), SetTagging(tagging))
+	c.Assert(err, IsNil)
+	err = s.bucket.DeleteObjectTagging(objectName)
+	c.Assert(err, IsNil)
+	taggingResult, err := s.bucket.GetObjectTagging(objectName)
+	c.Assert(err, IsNil)
+	c.Assert(len(taggingResult.Tags), Equals, 0)
+
+	//delete object tagging again
+	err = s.bucket.DeleteObjectTagging(objectName)
+	c.Assert(err, IsNil)
+
+	s.bucket.DeleteObject(objectName)
+}

+ 92 - 0
oss/client.go

@@ -219,6 +219,7 @@ func (client Client) GetBucketLocation(bucketName string) (string, error) {
 func (client Client) SetBucketACL(bucketName string, bucketACL ACLType) error {
 	headers := map[string]string{HTTPHeaderOssACL: string(bucketACL)}
 	params := map[string]interface{}{}
+	params["acl"] = nil
 	resp, err := client.do("PUT", bucketName, params, headers, nil)
 	if err != nil {
 		return err
@@ -650,6 +651,97 @@ func (client Client) GetBucketInfo(bucketName string) (GetBucketInfoResult, erro
 	}
 	defer resp.Body.Close()
 
+	err = xmlUnmarshal(resp.Body, &out)
+
+	// convert None to ""
+	if err == nil {
+		if out.BucketInfo.SseRule.KMSMasterKeyID == "None" {
+			out.BucketInfo.SseRule.KMSMasterKeyID = ""
+		}
+
+		if out.BucketInfo.SseRule.SSEAlgorithm == "None" {
+			out.BucketInfo.SseRule.SSEAlgorithm = ""
+		}
+	}
+	return out, err
+}
+
+// SetBucketEncryption set bucket encryption config
+// bucketName    the bucket name.
+// error    it's nil if no error, otherwise it's an error object.
+func (client Client) SetBucketEncryption(bucketName string, encryptionRule ServerEncryptionRule) error {
+	var err error
+	var bs []byte
+	bs, err = xml.Marshal(encryptionRule)
+
+	if err != nil {
+		return err
+	}
+
+	buffer := new(bytes.Buffer)
+	buffer.Write(bs)
+
+	contentType := http.DetectContentType(buffer.Bytes())
+	headers := map[string]string{}
+	headers[HTTPHeaderContentType] = contentType
+
+	params := map[string]interface{}{}
+	params["encryption"] = nil
+	resp, err := client.do("PUT", bucketName, params, headers, buffer)
+
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusOK})
+}
+
+// GetBucketEncryption get bucket encryption config
+// bucketName    the bucket name.
+// error    it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketEncryption(bucketName string) (GetBucketEncryptionResult, error) {
+	var out GetBucketEncryptionResult
+	params := map[string]interface{}{}
+	params["encryption"] = nil
+	resp, err := client.do("GET", bucketName, params, nil, nil)
+
+	if err != nil {
+		return out, err
+	}
+	defer resp.Body.Close()
+
+	err = xmlUnmarshal(resp.Body, &out)
+	return out, err
+}
+
+// DeleteBucketEncryption delete bucket encryption config
+// bucketName    the bucket name.
+// error    it's nil if no error, otherwise it's an error object.
+func (client Client) DeleteBucketEncryption(bucketName string) error {
+	params := map[string]interface{}{}
+	params["encryption"] = nil
+	resp, err := client.do("DELETE", bucketName, params, nil, nil)
+
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+	return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
+}
+
+// GetBucketStat get bucket stat
+// bucketName    the bucket name.
+// error    it's nil if no error, otherwise it's an error object.
+func (client Client) GetBucketStat(bucketName string) (GetBucketStatResult, error) {
+	var out GetBucketStatResult
+	params := map[string]interface{}{}
+	params["stat"] = nil
+	resp, err := client.do("GET", bucketName, params, nil, nil)
+	if err != nil {
+		return out, err
+	}
+	defer resp.Body.Close()
+
 	err = xmlUnmarshal(resp.Body, &out)
 	return out, err
 }

+ 178 - 0
oss/client_test.go

@@ -1839,3 +1839,181 @@ func (s *OssClientSuite) TestSetLimitUploadSpeed(c *C) {
 		c.Assert(err, NotNil)
 	}
 }
+
+func (s *OssClientSuite) TestBucketEncyptionError(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + randLowStr(5)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// SetBucketEncryption:AES256 ,"123"
+	encryptionRule := ServerEncryptionRule{}
+	encryptionRule.SSEDefault.SSEAlgorithm = string(AESAlgorithm)
+	encryptionRule.SSEDefault.KMSMasterKeyID = "123"
+
+	err = client.SetBucketEncryption(bucketName, encryptionRule)
+	c.Assert(err, NotNil)
+
+	// GetBucketEncryption
+	_, err = client.GetBucketEncryption(bucketName)
+	c.Assert(err, NotNil)
+
+	// Get default bucket info
+	bucketResult, err := client.GetBucketInfo(bucketName)
+	c.Assert(err, IsNil)
+
+	c.Assert(bucketResult.BucketInfo.SseRule.SSEAlgorithm, Equals, "")
+	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "")
+
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
+func (s *OssClientSuite) TestBucketEncyptionPutAndGetAndDelete(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + randLowStr(5)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// SetBucketEncryption:KMS ,""
+	encryptionRule := ServerEncryptionRule{}
+	encryptionRule.SSEDefault.SSEAlgorithm = string(KMSAlgorithm)
+
+	err = client.SetBucketEncryption(bucketName, encryptionRule)
+	c.Assert(err, IsNil)
+
+	// GetBucketEncryption
+	getResult, err := client.GetBucketEncryption(bucketName)
+	c.Assert(err, IsNil)
+
+	// check encryption value
+	c.Assert(encryptionRule.SSEDefault.SSEAlgorithm, Equals, getResult.SSEDefault.SSEAlgorithm)
+	c.Assert(encryptionRule.SSEDefault.KMSMasterKeyID, Equals, getResult.SSEDefault.KMSMasterKeyID)
+
+	// delete bucket encyption
+	err = client.DeleteBucketEncryption(bucketName)
+	c.Assert(err, IsNil)
+
+	// GetBucketEncryption failure
+	_, err = client.GetBucketEncryption(bucketName)
+	c.Assert(err, NotNil)
+
+	// Get default bucket info
+	bucketResult, err := client.GetBucketInfo(bucketName)
+	c.Assert(err, IsNil)
+
+	c.Assert(bucketResult.BucketInfo.SseRule.SSEAlgorithm, Equals, "")
+	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "")
+
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
+func (s *OssClientSuite) TestBucketEncyptionPutObjectSuccess(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + randLowStr(5)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// SetBucketEncryption:KMS ,""
+	encryptionRule := ServerEncryptionRule{}
+	encryptionRule.SSEDefault.SSEAlgorithm = string(KMSAlgorithm)
+
+	err = client.SetBucketEncryption(bucketName, encryptionRule)
+	c.Assert(err, IsNil)
+
+	// GetBucketEncryption
+	getResult, err := client.GetBucketEncryption(bucketName)
+	c.Assert(err, IsNil)
+
+	// check encryption value
+	c.Assert(encryptionRule.SSEDefault.SSEAlgorithm, Equals, getResult.SSEDefault.SSEAlgorithm)
+	c.Assert(encryptionRule.SSEDefault.KMSMasterKeyID, Equals, getResult.SSEDefault.KMSMasterKeyID)
+
+	// Get default bucket info
+	bucketResult, err := client.GetBucketInfo(bucketName)
+	c.Assert(err, IsNil)
+
+	c.Assert(bucketResult.BucketInfo.SseRule.SSEAlgorithm, Equals, "KMS")
+	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "")
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
+func (s *OssClientSuite) TestBucketEncyptionPutObjectError(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + randLowStr(5)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// SetBucketEncryption:KMS ,""
+	encryptionRule := ServerEncryptionRule{}
+	encryptionRule.SSEDefault.SSEAlgorithm = string(KMSAlgorithm)
+	encryptionRule.SSEDefault.KMSMasterKeyID = "123"
+
+	err = client.SetBucketEncryption(bucketName, encryptionRule)
+	c.Assert(err, IsNil)
+
+	// GetBucketEncryption
+	getResult, err := client.GetBucketEncryption(bucketName)
+	c.Assert(err, IsNil)
+
+	// check encryption value
+	c.Assert(encryptionRule.SSEDefault.SSEAlgorithm, Equals, getResult.SSEDefault.SSEAlgorithm)
+	c.Assert(encryptionRule.SSEDefault.KMSMasterKeyID, Equals, getResult.SSEDefault.KMSMasterKeyID)
+
+	// Get default bucket info
+	bucketResult, err := client.GetBucketInfo(bucketName)
+	c.Assert(err, IsNil)
+
+	c.Assert(bucketResult.BucketInfo.SseRule.SSEAlgorithm, Equals, "KMS")
+	c.Assert(bucketResult.BucketInfo.SseRule.KMSMasterKeyID, Equals, "123")
+
+	// put and get object failure
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// put object failure
+	objectName := objectNamePrefix + randStr(8)
+	context := randStr(100)
+	err = bucket.PutObject(objectName, strings.NewReader(context))
+	c.Assert(err, NotNil)
+
+	err = client.DeleteBucket(bucketName)
+	c.Assert(err, IsNil)
+}
+
+func (s *OssClientSuite) TestGetBucketStat(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	bucketName := bucketNamePrefix + randLowStr(5)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	bucket, err := client.Bucket(bucketName)
+	c.Assert(err, IsNil)
+
+	// put object
+	objectName := objectNamePrefix + randLowStr(5)
+	err = bucket.PutObject(objectName, strings.NewReader(randStr(10)))
+	c.Assert(err, IsNil)
+
+	bucket.DeleteObject(objectName)
+	err = bucket.PutObject(objectName, strings.NewReader(randStr(10)))
+	c.Assert(err, IsNil)
+	bucket.DeleteObject(objectName)
+
+	_, err = client.GetBucketStat(bucketName)
+	c.Assert(err, IsNil)
+
+	client.DeleteBucket(bucketName)
+}

+ 10 - 1
oss/conn.go

@@ -27,7 +27,16 @@ type Conn struct {
 	client *http.Client
 }
 
-var signKeyList = []string{"acl", "uploads", "location", "cors", "logging", "website", "referer", "lifecycle", "delete", "append", "tagging", "objectMeta", "uploadId", "partNumber", "security-token", "position", "img", "style", "styleName", "replication", "replicationProgress", "replicationLocation", "cname", "bucketInfo", "comp", "qos", "live", "status", "vod", "startTime", "endTime", "symlink", "x-oss-process", "response-content-type", "response-content-language", "response-expires", "response-cache-control", "response-content-disposition", "response-content-encoding", "udf", "udfName", "udfImage", "udfId", "udfImageDesc", "udfApplication", "comp", "udfApplicationLog", "restore", "callback", "callback-var", "policy"}
+var signKeyList = []string{"acl", "uploads", "location", "cors", "logging",
+	"website", "referer", "lifecycle", "delete", "append", "tagging", "objectMeta",
+	"uploadId", "partNumber", "security-token", "position", "img", "style", "styleName",
+	"replication", "replicationProgress", "replicationLocation", "cname", "bucketInfo",
+	"comp", "qos", "live", "status", "vod", "startTime", "endTime", "symlink",
+	"x-oss-process", "response-content-type", "response-content-language",
+	"response-expires", "response-cache-control", "response-content-disposition",
+	"response-content-encoding", "udf", "udfName", "udfImage", "udfId", "udfImageDesc",
+	"udfApplication", "comp", "udfApplicationLog", "restore", "callback", "callback-var",
+	"policy", "tagging", "stat", "encryption"}
 
 // init initializes Conn
 func (conn *Conn) init(config *Config, urlMaker *urlMaker, client *http.Client) error {

+ 22 - 1
oss/const.go

@@ -30,6 +30,25 @@ const (
 	MetaReplace MetadataDirectiveType = "REPLACE"
 )
 
+// TaggingDirectiveType specifying whether use the tagging of source object when copying object.
+type TaggingDirectiveType string
+
+const (
+	// TaggingCopy the target object's tagging is copied from the source one
+	TaggingCopy TaggingDirectiveType = "COPY"
+
+	// TaggingReplace the target object's tagging is created as part of the copy request (not same as the source one)
+	TaggingReplace TaggingDirectiveType = "REPLACE"
+)
+
+// AlgorithmType specifying the server side encryption algorithm name
+type AlgorithmType string
+
+const (
+	KMSAlgorithm AlgorithmType = "KMS"
+	AESAlgorithm AlgorithmType = "AES256"
+)
+
 // StorageClassType bucket storage type
 type StorageClassType string
 
@@ -118,7 +137,9 @@ const (
 	HTTPHeaderOssStorageClass                = "X-Oss-Storage-Class"
 	HTTPHeaderOssCallback                    = "X-Oss-Callback"
 	HTTPHeaderOssCallbackVar                 = "X-Oss-Callback-Var"
-	HTTPHeaderOSSRequester                   = "X-Oss-Request-Payer"
+	HTTPHeaderOssRequester                   = "X-Oss-Request-Payer"
+	HTTPHeaderOssTagging                     = "X-Oss-Tagging"
+	HTTPHeaderOssTaggingDirective            = "X-Oss-Tagging-Directive"
 )
 
 // HTTP Param

+ 23 - 1
oss/option.go

@@ -3,6 +3,7 @@ package oss
 import (
 	"fmt"
 	"net/http"
+	"net/url"
 	"strconv"
 	"strings"
 	"time"
@@ -199,7 +200,28 @@ func CallbackVar(callbackVar string) Option {
 
 // RequestPayer is an option to set payer who pay for the request
 func RequestPayer(payerType PayerType) Option {
-	return setHeader(HTTPHeaderOSSRequester, string(payerType))
+	return setHeader(HTTPHeaderOssRequester, string(payerType))
+}
+
+// SetTagging is an option to set object tagging
+func SetTagging(tagging Tagging) Option {
+	if len(tagging.Tags) == 0 {
+		return nil
+	}
+
+	taggingValue := ""
+	for index, tag := range tagging.Tags {
+		if index != 0 {
+			taggingValue += "&"
+		}
+		taggingValue += url.QueryEscape(tag.Key) + "=" + url.QueryEscape(tag.Value)
+	}
+	return setHeader(HTTPHeaderOssTagging, taggingValue)
+}
+
+// TaggingDirective is an option to set X-Oss-Metadata-Directive header
+func TaggingDirective(directive TaggingDirectiveType) Option {
+	return setHeader(HTTPHeaderOssTaggingDirective, string(directive))
 }
 
 // Delimiter is an option to set delimiler parameter

+ 54 - 8
oss/type.go

@@ -47,6 +47,7 @@ type LifecycleRule struct {
 	ID                   string                         `xml:"ID,omitempty"`                   // The rule ID
 	Prefix               string                         `xml:"Prefix"`                         // The object key prefix
 	Status               string                         `xml:"Status"`                         // The rule status (enabled or not)
+	Tags                 []Tag                          `xml:"Tag,omitempty"`                  // the tags property
 	Expiration           *LifecycleExpiration           `xml:"Expiration,omitempty"`           // The expiration property
 	Transitions          []LifecycleTransition          `xml:"Transition,omitempty"`           // The transition property
 	AbortMultipartUpload *LifecycleAbortMultipartUpload `xml:"AbortMultipartUpload,omitempty"` // The AbortMultipartUpload property
@@ -227,14 +228,21 @@ type GetBucketInfoResult struct {
 // BucketInfo defines Bucket information
 type BucketInfo struct {
 	XMLName          xml.Name  `xml:"Bucket"`
-	Name             string    `xml:"Name"`                    // Bucket name
-	Location         string    `xml:"Location"`                // Bucket datacenter
-	CreationDate     time.Time `xml:"CreationDate"`            // Bucket creation time
-	ExtranetEndpoint string    `xml:"ExtranetEndpoint"`        // Bucket external endpoint
-	IntranetEndpoint string    `xml:"IntranetEndpoint"`        // Bucket internal endpoint
-	ACL              string    `xml:"AccessControlList>Grant"` // Bucket ACL
-	Owner            Owner     `xml:"Owner"`                   // Bucket owner
-	StorageClass     string    `xml:"StorageClass"`            // Bucket storage class
+	Name             string    `xml:"Name"`                     // Bucket name
+	Location         string    `xml:"Location"`                 // Bucket datacenter
+	CreationDate     time.Time `xml:"CreationDate"`             // Bucket creation time
+	ExtranetEndpoint string    `xml:"ExtranetEndpoint"`         // Bucket external endpoint
+	IntranetEndpoint string    `xml:"IntranetEndpoint"`         // Bucket internal endpoint
+	ACL              string    `xml:"AccessControlList>Grant"`  // Bucket ACL
+	Owner            Owner     `xml:"Owner"`                    // Bucket owner
+	StorageClass     string    `xml:"StorageClass"`             // Bucket storage class
+	SseRule          SSERule   `xml:"ServerSideEncryptionRule"` // Bucket ServerSideEncryptionRule
+}
+
+type SSERule struct {
+	XMLName        xml.Name `xml:"ServerSideEncryptionRule"` // Bucket ServerSideEncryptionRule
+	KMSMasterKeyID string   `xml:"KMSMasterKeyID"`           // Bucket KMSMasterKeyID
+	SSEAlgorithm   string   `xml:"SSEAlgorithm"`             // Bucket SSEAlgorithm
 }
 
 // ListObjectsResult defines the result from ListObjects request
@@ -593,3 +601,41 @@ type LiveChannelInfo struct {
 	PublishUrls  []string  `xml:"PublishUrls>Url"` //push urls list
 	PlayUrls     []string  `xml:"PlayUrls>Url"`    //play urls list
 }
+
+// Tag a tag for the object
+type Tag struct {
+	XMLName xml.Name `xml:"Tag"`
+	Key     string   `xml:"Key"`
+	Value   string   `xml:"Value"`
+}
+
+// ObjectTagging tagset for the object
+type Tagging struct {
+	XMLName xml.Name `xml:"Tagging"`
+	Tags    []Tag    `xml:"TagSet>Tag,omitempty"`
+}
+
+type GetObjectTaggingResult Tagging
+
+// Server Encryption rule for the bucket
+type ServerEncryptionRule struct {
+	XMLName    xml.Name       `xml:"ServerSideEncryptionRule"`
+	SSEDefault SSEDefaultRule `xml:"ApplyServerSideEncryptionByDefault"`
+}
+
+// Server Encryption deafult rule for the bucket
+type SSEDefaultRule struct {
+	XMLName        xml.Name `xml:"ApplyServerSideEncryptionByDefault"`
+	SSEAlgorithm   string   `xml:"SSEAlgorithm"`
+	KMSMasterKeyID string   `xml:"KMSMasterKeyID"`
+}
+
+type GetBucketEncryptionResult ServerEncryptionRule
+
+type BucketStat struct {
+	XMLName              xml.Name `xml:"BucketStat"`
+	Storage              int64    `xml:"Storage"`
+	ObjectCount          int64    `xml:ObjectCount`
+	MultipartUploadCount int64    `xml:MultipartUploadCount`
+}
+type GetBucketStatResult BucketStat

+ 1 - 1
oss/upload.go

@@ -94,7 +94,7 @@ func getRoutines(options []Option) int {
 
 // getPayer return the payer of the request
 func getPayer(options []Option) string {
-	payerOpt, err := findOption(options, HTTPHeaderOSSRequester, nil)
+	payerOpt, err := findOption(options, HTTPHeaderOssRequester, nil)
 	if err != nil || payerOpt == nil {
 		return ""
 	}

+ 41 - 24
sample.go

@@ -3,34 +3,51 @@
 package main
 
 import (
+	"flag"
 	"fmt"
-    
+	"os"
+
 	"github.com/aliyun/aliyun-oss-go-sdk/sample"
 )
 
-func main() {
-	sample.CreateBucketSample()
-	sample.NewBucketSample()
-	sample.ListBucketsSample()
-	sample.BucketACLSample()
-	sample.BucketLifecycleSample()
-	sample.BucketRefererSample()
-	sample.BucketLoggingSample()
-	sample.BucketCORSSample()
-
-	sample.ObjectACLSample()
-	sample.ObjectMetaSample()
-	sample.ListObjectsSample()
-	sample.DeleteObjectSample()
-	sample.AppendObjectSample()
-	sample.CopyObjectSample()
-	sample.PutObjectSample()
-	sample.GetObjectSample()
-
-	sample.CnameSample()
-	sample.SignURLSample()
+// sampleMap contains all samples
+var sampleMap = map[string]interface{}{
+	"CreateBucketSample":     sample.CreateBucketSample,
+	"NewBucketSample":        sample.NewBucketSample,
+	"ListBucketsSample":      sample.ListBucketsSample,
+	"BucketACLSample":        sample.BucketACLSample,
+	"BucketLifecycleSample":  sample.BucketLifecycleSample,
+	"BucketRefererSample":    sample.BucketRefererSample,
+	"BucketLoggingSample":    sample.BucketLoggingSample,
+	"BucketCORSSample":       sample.BucketCORSSample,
+	"ObjectACLSample":        sample.ObjectACLSample,
+	"ObjectMetaSample":       sample.ObjectMetaSample,
+	"ListObjectsSample":      sample.ListObjectsSample,
+	"DeleteObjectSample":     sample.DeleteObjectSample,
+	"AppendObjectSample":     sample.AppendObjectSample,
+	"CopyObjectSample":       sample.CopyObjectSample,
+	"PutObjectSample":        sample.PutObjectSample,
+	"GetObjectSample":        sample.GetObjectSample,
+	"CnameSample":            sample.CnameSample,
+	"SignURLSample":          sample.SignURLSample,
+	"ArchiveSample":          sample.ArchiveSample,
+	"ObjectTaggingSample":    sample.ObjectTaggingSample,
+	"BucketEncryptionSample": sample.BucketEncryptionSample,
+}
 
-	sample.ArchiveSample()
+func main() {
+	var name string
+	flag.StringVar(&name, "name", "", "Waiting for a sample of execution")
+	flag.Parse()
 
-	fmt.Println("All samples completed")
+	if len(name) <= 0 {
+		fmt.Println("please enter your sample's name. like '-name CreateBucketSample'")
+		os.Exit(-1)
+	} else {
+		if sampleMap[name] == nil {
+			fmt.Println("the " + name + "is not exist.")
+			os.Exit(-1)
+		}
+		sampleMap[name].(func())()
+	}
 }

+ 51 - 0
sample/bucket_encryption.go

@@ -0,0 +1,51 @@
+package sample
+
+import (
+	"fmt"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// BucketEncryptionSample shows how to get and set the bucket encryption Algorithm
+func BucketEncryptionSample() {
+	// New client
+	client, err := oss.New(endpoint, accessID, accessKey)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Create a bucket with default parameters
+	err = client.CreateBucket(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// SetBucketEncryption:AES256 ,"123"
+	encryptionRule := oss.ServerEncryptionRule{}
+	encryptionRule.SSEDefault.SSEAlgorithm = string(oss.AESAlgorithm)
+	err = client.SetBucketEncryption(bucketName, encryptionRule)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Get bucket encryption
+	encryptionResult, err := client.GetBucketEncryption(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+	fmt.Println("Bucket Encryption:", encryptionResult)
+
+	// Delete the bucket
+	err = client.DeleteBucketEncryption(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Delete the object and bucket
+	err = DeleteTestBucketAndObject(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+
+	fmt.Println("BucketEncryptionSample completed")
+}

+ 26 - 14
sample/bucket_lifecycle.go

@@ -20,7 +20,7 @@ func BucketLifecycleSample() {
 		HandleError(err)
 	}
 
-	// Case 1: Set the lifecycle. The rule ID is rule1 and the applied objects' prefix is one and expired time is 11/11/2015
+	// Case 1: Set the lifecycle. The rule ID is rule1 and the applied objects' prefix is one and the last modified Date is before 2015/11/11
 	expriation := oss.LifecycleExpiration{
 		CreatedBeforeDate: "2015-11-11T00:00:00.000Z",
 	}
@@ -36,14 +36,14 @@ func BucketLifecycleSample() {
 		HandleError(err)
 	}
 
-	// Get the bucket's lifecycle
+	// Case 2: Get the bucket's lifecycle
 	lc, err := client.GetBucketLifecycle(bucketName)
 	if err != nil {
 		HandleError(err)
 	}
 	fmt.Printf("Bucket Lifecycle:%v, %v\n", lc.Rules, *lc.Rules[0].Expiration)
 
-	// Case 2: Set the lifecycle, The rule ID is id2 and the applied objects' prefix is two and the expired time is three days after the object created.
+	// Case 3: Set the lifecycle, The rule ID is rule2 and the applied objects' prefix is two. The object start with the prefix will be transited to IA storage Type 3 days latter, and to archive storage type 30 days latter
 	transitionIA := oss.LifecycleTransition{
 		Days:         3,
 		StorageClass: oss.StorageIA,
@@ -64,13 +64,7 @@ func BucketLifecycleSample() {
 		HandleError(err)
 	}
 
-	// Get the bucket's lifecycle
-	lc, err = client.GetBucketLifecycle(bucketName)
-	if err != nil {
-		HandleError(err)
-	}
-	fmt.Printf("Bucket Lifecycle:%v\n", lc.Rules)
-
+	// Case 4: Set the lifecycle, The rule ID is rule3 and the applied objects' prefix is three. The object start with the prefix will be transited to IA storage Type 3 days latter, and to archive storage type 30 days latter, the uncompleted multipart upload will be abort 3 days latter.
 	abortMPU := oss.LifecycleAbortMultipartUpload{
 		Days: 3,
 	}
@@ -86,14 +80,32 @@ func BucketLifecycleSample() {
 		HandleError(err)
 	}
 
-	// Get the bucket's lifecycle
-	lc, err = client.GetBucketLifecycle(bucketName)
+	// Case 5: Set the lifecycle. The rule ID is rule4 and the applied objects' has the tagging which prefix is four and the last modified Date is before 2015/11/11
+	expriation = oss.LifecycleExpiration{
+		CreatedBeforeDate: "2015-11-11T00:00:00.000Z",
+	}
+	tag1 := oss.Tag{
+		Key:   "key1",
+		Value: "value1",
+	}
+	tag2 := oss.Tag{
+		Key:   "key2",
+		Value: "value2",
+	}
+	rule4 := oss.LifecycleRule{
+		ID:         "rule4",
+		Prefix:     "four",
+		Status:     "Enabled",
+		Tags:       []oss.Tag{tag1, tag2},
+		Expiration: &expriation,
+	}
+	rules = []oss.LifecycleRule{rule4}
+	err = client.SetBucketLifecycle(bucketName, rules)
 	if err != nil {
 		HandleError(err)
 	}
-	fmt.Printf("Bucket Lifecycle:%v, %v\n", lc.Rules, *lc.Rules[1].AbortMultipartUpload)
 
-	// Delete bucket's Lifecycle
+	// Case 6: Delete bucket's Lifecycle
 	err = client.DeleteBucketLifecycle(bucketName)
 	if err != nil {
 		HandleError(err)

+ 19 - 2
sample/copy_object.go

@@ -106,8 +106,25 @@ func CopyObjectSample() {
 
 	// Case 7: Set the storage classes.OSS provides three storage classes: Standard, Infrequent Access, and Archive.
 	// Copy a object in the same bucket, and set object's storage-class to Archive.
-	_, rr := bucket.CopyObject(objectKey, objectKey+"DestArchive", oss.ObjectStorageClass("Archive"))
-	if rr != nil {
+	_, err = bucket.CopyObject(objectKey, objectKey+"DestArchive", oss.ObjectStorageClass("Archive"))
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Case 8: Copy object with tagging, the value of tagging directive is REPLACE
+	tag1 := oss.Tag{
+		Key:   "key1",
+		Value: "value1",
+	}
+	tag2 := oss.Tag{
+		Key:   "key2",
+		Value: "value2",
+	}
+	tagging := oss.Tagging{
+		Tags: []oss.Tag{tag1, tag2},
+	}
+	_, err = bucket.CopyObject(objectKey, objectKey+"WithTagging", oss.SetTagging(tagging), oss.TaggingDirective(oss.TaggingReplace))
+	if err != nil {
 		HandleError(err)
 	}
 

+ 75 - 0
sample/object_tagging.go

@@ -0,0 +1,75 @@
+package sample
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+// ObjectTaggingSample shows how to set and get object Tagging
+func ObjectTaggingSample() {
+	// Create bucket
+	bucket, err := GetTestBucket(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Create object
+	err = bucket.PutObject(objectKey, strings.NewReader("ObjectTaggingSample"))
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Case 1: Set Tagging of object
+	tag1 := oss.Tag{
+		Key:   "key1",
+		Value: "value1",
+	}
+	tag2 := oss.Tag{
+		Key:   "key2",
+		Value: "value2",
+	}
+	tagging := oss.Tagging{
+		Tags: []oss.Tag{tag1, tag2},
+	}
+	err = bucket.PutObjectTagging(objectKey, tagging)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Case 2: Get Tagging of object
+	taggingResult, err := bucket.GetObjectTagging(objectKey)
+	if err != nil {
+		HandleError(err)
+	}
+	fmt.Printf("Object Tagging: %v\n", taggingResult)
+
+	tag3 := oss.Tag{
+		Key:   "key3",
+		Value: "value3",
+	}
+
+	// Case 3: Put object with tagging
+	tagging = oss.Tagging{
+		Tags: []oss.Tag{tag1, tag2, tag3},
+	}
+	err = bucket.PutObject(objectKey, strings.NewReader("ObjectTaggingSample"), oss.SetTagging(tagging))
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Case 4: Delete Tagging of object
+	err = bucket.DeleteObjectTagging(objectKey)
+	if err != nil {
+		HandleError(err)
+	}
+
+	// Delete object and bucket
+	err = DeleteTestBucketAndObject(bucketName)
+	if err != nil {
+		HandleError(err)
+	}
+
+	fmt.Println("ObjectACLSample completed")
+}