├── .gitignore ├── LICENSE ├── README.md ├── Uploader v1.0.2.alfredworkflow ├── bin ├── pngpaste └── upload ├── composer.json ├── composer.lock ├── icon.png └── src ├── Helper.php ├── History.php ├── Provider ├── ProviderInterface.php ├── QiniuProvider.php ├── S3Provider.php └── UpyunProvider.php └── Uploader.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | 4 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 6 | # composer.lock 7 | 8 | info.plist 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## alfred-uploader 2 | 3 | An Alfred workflow to help you upload local images or image from clipboard to cloud storage provider (support [s3](https://aws.amazon.com/s3/), [upyun](https://www.upyun.com/), [qiniu](http://www.qiniu.com/) now), and get back the public access url. 4 | 5 | ![](http://self-storage.b0.upaiyun.com/2016/11/23/147986500608518950.gif) 6 | 7 | ### Why create this? 8 | 9 | Markdown is a great language to write a document, and generate a good looking. But it is unpleasant when you insert a local image into your document. If I share the document including some local images with others, the images will be lost. So I should upload the images to a cloud storage provider at first, and copy back the access url into my document. It's so complicated and repetitive. The real terrible thing is to write a document, I want to insert a image, after I finished these fucking steps, back to the document, I have forgotten what to write. 10 | 11 | [Alfred](http://www.alfredapp.com/) is a efficient tool for Mac. There is a great feature called **Workflows**, we can create a workflow to do the complicated and repetitive work. I'm a heavy user of Alfred, I use it to find files, open apps, translate, calculate and so on. Only you can not think, no you can not do. 12 | 13 | I can not stand to spend a lot of time waiting for the upload and copy again and again. So I create a workflow called **Uploader** to connect file input, image processing and url output. 14 | 15 | With **Uploader**, if I want to insert a image while writing, use hotkey to open alfred, input "upload" keyword, select the image to upload, continue writing, just keep an eye on the upload success notification, paste the access url, that's all. My focus was always on the document I'm writing, alfred only disturbed for 1 to 3 seconds. 16 | 17 | The all images in this document was uploaded by Uploader proudly. Now let me introduce Uploader to you, you can get it from [alfred-uploader](https://github.com/iammapping/alfred-uploader). 18 | 19 | 20 | 21 | ### Setup 22 | 23 | Download [the latest release](https://github.com/iammapping/alfred-uploader/releases) and double-click the workflow file (with a *.alfredworkflow* extension). Alfred will install it automatically. 24 | 25 | After installed, you can find it in **Alfred Preferences -> Workflows list**. 26 | 27 | Before we get started, we have to set some variables, click the "**[x]**" icon button specified with red arrow below. 28 | 29 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147963249017197318.png) 30 | 31 | Fill the variables into the right list on the pop-up modal, then save. 32 | 33 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147963276083921517.png) 34 | 35 | 36 | 37 | #### Variables 38 | 39 | * `UPLOAD_PROVIDER` 40 | * **Required** 41 | * The cloud storage provider, such as: S3, Upyun, Qiniu... 42 | * Support `s3` , `upyun`, `qiniu` now, all provider name shoud be lower case 43 | * `UPLOAD_HISTORY_COUNT` 44 | * **Optional**, Default is `50` 45 | * The maximum number of records to save 46 | * `USE_RAW_FILENAME` 47 | * **Optional**, Default is `fasle` 48 | * Whether use the original filename 49 | * The image uploaded will be given a random numeric filename, looks like *147963249017197318.png*. 50 | * If `true`, the image upload with the original filename, but some special chars (space and ?#%:) will replace with "_" 51 | * The specific variables for `s3` 52 | * `S3_BUCKET` 53 | * **Required** 54 | * The bucket name 55 | * `S3_REGION` 56 | * **Required** 57 | * The region of the bucket. e.g. ap-southeast-1 58 | * `S3_ACCESS_KEY` 59 | * **Required** 60 | * Your AWS access key ID 61 | * `S3_SECRET_KEY` 62 | * **Required** 63 | * Your AWS secret access key 64 | * `S3_BASEURL` 65 | * **Optinal**, Default is the returned `ObjectURL` 66 | * The base url of the access url 67 | * You can change to the domain of yourself, only when you have config the **CNAME** redirector 68 | * The specific variables for `upyun` 69 | * `UPYUN_BUCKET` 70 | * **Required** 71 | * The service name created at **Upyun Console** 72 | * `UPYUN_USER` 73 | * **Required** 74 | * The operator name 75 | * Make sure you have granted the operator reading and writing 76 | * `UPYUN_PWD` 77 | * **Required** 78 | * The operator password 79 | * Be careful, it saved as plain 80 | * `UPYUN_USE_STREAM` 81 | * **Optional**, Default is `false` 82 | * Whether enable stream mode 83 | * Upload with stream mode will save memory 84 | * `UPYUN_BASEURL` 85 | * **Optinal**, Default is `http://{UPYUN_BUCKET}.b0.upaiyun.com` 86 | * The base url of the access url 87 | * You can change to the domain of yourself, only when you have config the **CNAME** redirector 88 | * The specific variables for `qiniu` 89 | * `QINIU_BUCKET` 90 | * **Required** 91 | * The bucket name 92 | * `QINIU_ACCESS_KEY` 93 | * **Required** 94 | * Your access key created at [https://portal.qiniu.com/user/key](https://portal.qiniu.com/user/key) 95 | * `QINIU_SECRET_KEY` 96 | * **Required** 97 | * Your secret key created at [https://portal.qiniu.com/user/key](https://portal.qiniu.com/user/key) 98 | * `QINIU_BASEURL` 99 | * **Required** 100 | * The base url of the access url 101 | * You can change to the domain of yourself, only when you have config the **CNAME** redirector 102 | 103 | > Attention: 104 | > 105 | > * Don't export your secret variables, unless you want to share your storage. 106 | > * All `BASEURL` variable should be without the tail '/'. 107 | 108 | 109 | 110 | ### Usage 111 | 112 | Everything is ready. Let's upload. 113 | 114 | #### Upload 115 | 116 | There are three ways to upload: 117 | 118 | 1. Input "upload" keyword, pick a file. You can also filter the files with query. 119 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147964465139413269.png) 120 | 121 | 122 | 123 | 2. locate a file in Alfred, select "Uploader" operation 124 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147964477679258266.png) 125 | 126 | 127 | 3. select files in Finder, press hotkey "cmd+shift+s". I highly recommend this way, because it support multi files once. 128 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147964528913390042.png) 129 | 130 | 131 | 132 | Once you receive a notification said "😀Image upload complete", it means the local image has been uploaded to remote, and the access url is ready in your clipboard, you can paste it directly. 133 | 134 | > Tips: The first way, if you select a file with "alt (option)" key pressing, the result url will wrap a markdown img tag (\!\[\]\({url}\)) 135 | 136 | 137 | 138 | #### Uploaded 139 | 140 | Use "uploaded" keyword to lookup the uploaded images, keeping 50 records by default, query filter alse support. 141 | 142 | ![](http://self-storage.b0.upaiyun.com/2016/11/20/147964637559652782.png) 143 | 144 | As shown above, you can see the access url, the original file path and the upload time. Once you select a image, the access url copied, then paste it to anywhere you want. From last tips learned, the "alt" key also works here. 145 | 146 | 147 | 148 | ### Change Logs 149 | 150 | #### v1.0.2 151 | 152 | * support uploading image from clipboard (thanks [@Tony Xu](https://github.com/tonyxu-io)) 153 | 154 | 155 | #### v1.0.1 156 | 157 | * support S3, Qiniu 158 | 159 | 160 | 161 | 162 | ### Contribute 163 | 164 | Send me a pull request, or commit an issue. 165 | 166 | 167 | 168 | ### Lincese 169 | 170 | MIT 171 | -------------------------------------------------------------------------------- /Uploader v1.0.2.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iammapping/alfred-uploader/30cd0e5fa5d97808a292e33690a9b88ad354061a/Uploader v1.0.2.alfredworkflow -------------------------------------------------------------------------------- /bin/pngpaste: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iammapping/alfred-uploader/30cd0e5fa5d97808a292e33690a9b88ad354061a/bin/pngpaste -------------------------------------------------------------------------------- /bin/upload: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 'public.png', 117 | 'jpg' => 'public.jpeg', 118 | 'jpeg' => 'public.jpeg', 119 | 'gif' => 'com.compuserve.gif' 120 | ); 121 | foreach (Upload\History::get(isset($query[0]) ? $query[0] : '') as $it) { 122 | $items[] = array( 123 | 'title' => $it['url'], 124 | 'subtitle' => PrettyDateTime\PrettyDateTime::parse((new \DateTime())->setTimestamp($it['time'])) . '. ' . $it['file'], 125 | 'arg' => $it['url'], 126 | 'icon' => array( 127 | 'type' => 'filetype', 128 | 'path' => $extensionMap[strtolower(pathinfo($it['file'], PATHINFO_EXTENSION))] 129 | ), 130 | 'quicklookurl' => $it['url'] 131 | ); 132 | } 133 | $output['items'] = $items; 134 | 135 | echo json_encode($output); 136 | } else { 137 | // upload image to remote 138 | $uploader = new Upload\Uploader($query, $env); 139 | try { 140 | $uploaded = $uploader->upload(); 141 | // save upload history 142 | Upload\History::set($uploaded); 143 | 144 | foreach ($uploaded as $upd) { 145 | if ($wrapMarkdown) { 146 | // wrap url with markdown 147 | echo "![]({$upd['url']})" . PHP_EOL; 148 | } else { 149 | echo $upd['url'] . PHP_EOL; 150 | } 151 | } 152 | } catch (\Exception $ex) { 153 | echo $ex->getMessage(); 154 | } 155 | 156 | if ($fromClipboard) { 157 | // clean the tmp file generate from clipboard 158 | @unlink($query[0]); 159 | } 160 | } 161 | 162 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mapping/alfred-img-uploader", 3 | "description": "An Alfred workflow to help you upload local images to cloud storage provider, and get back the public access url.", 4 | "version": "1.0.2", 5 | "type": "project", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "iammapping", 10 | "email": "meibing_240@126.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.3", 15 | "upyun/sdk": "^2.2", 16 | "danielstjules/php-pretty-datetime": "dev-master", 17 | "qiniu/php-sdk": "^7.1", 18 | "aws/aws-sdk-php": "^3.19" 19 | }, 20 | "autoload": { 21 | "psr-0": { 22 | "Upload": "src/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "732616a91e79f98c4d6fc73201130db5", 8 | "content-hash": "d7424e89f23eac6211d821be487a4d57", 9 | "packages": [ 10 | { 11 | "name": "aws/aws-sdk-php", 12 | "version": "3.19.32", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/aws/aws-sdk-php.git", 16 | "reference": "4c3093e320c4a34e30327043d24099787b702665" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4c3093e320c4a34e30327043d24099787b702665", 21 | "reference": "4c3093e320c4a34e30327043d24099787b702665", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "guzzlehttp/guzzle": "^5.3.1|^6.2.1", 26 | "guzzlehttp/promises": "~1.0", 27 | "guzzlehttp/psr7": "~1.3.1", 28 | "mtdowling/jmespath.php": "~2.2", 29 | "php": ">=5.5" 30 | }, 31 | "require-dev": { 32 | "andrewsville/php-token-reflection": "^1.4", 33 | "aws/aws-php-sns-message-validator": "~1.0", 34 | "behat/behat": "~3.0", 35 | "doctrine/cache": "~1.4", 36 | "ext-dom": "*", 37 | "ext-json": "*", 38 | "ext-openssl": "*", 39 | "ext-pcre": "*", 40 | "ext-simplexml": "*", 41 | "ext-spl": "*", 42 | "nette/neon": "^2.3", 43 | "phpunit/phpunit": "~4.0|~5.0", 44 | "psr/cache": "^1.0" 45 | }, 46 | "suggest": { 47 | "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", 48 | "doctrine/cache": "To use the DoctrineCacheAdapter", 49 | "ext-curl": "To send requests using cURL", 50 | "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages" 51 | }, 52 | "type": "library", 53 | "extra": { 54 | "branch-alias": { 55 | "dev-master": "3.0-dev" 56 | } 57 | }, 58 | "autoload": { 59 | "psr-4": { 60 | "Aws\\": "src/" 61 | }, 62 | "files": [ 63 | "src/functions.php" 64 | ] 65 | }, 66 | "notification-url": "https://packagist.org/downloads/", 67 | "license": [ 68 | "Apache-2.0" 69 | ], 70 | "authors": [ 71 | { 72 | "name": "Amazon Web Services", 73 | "homepage": "http://aws.amazon.com" 74 | } 75 | ], 76 | "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", 77 | "homepage": "http://aws.amazon.com/sdkforphp", 78 | "keywords": [ 79 | "amazon", 80 | "aws", 81 | "cloud", 82 | "dynamodb", 83 | "ec2", 84 | "glacier", 85 | "s3", 86 | "sdk" 87 | ], 88 | "time": "2016-11-22 23:10:38" 89 | }, 90 | { 91 | "name": "danielstjules/php-pretty-datetime", 92 | "version": "dev-master", 93 | "source": { 94 | "type": "git", 95 | "url": "https://github.com/danielstjules/php-pretty-datetime.git", 96 | "reference": "d4bb790e8761160b2bb993df46a0042fcc6195b3" 97 | }, 98 | "dist": { 99 | "type": "zip", 100 | "url": "https://api.github.com/repos/danielstjules/php-pretty-datetime/zipball/d4bb790e8761160b2bb993df46a0042fcc6195b3", 101 | "reference": "d4bb790e8761160b2bb993df46a0042fcc6195b3", 102 | "shasum": "" 103 | }, 104 | "require": { 105 | "php": ">=5.3.0" 106 | }, 107 | "type": "library", 108 | "autoload": { 109 | "psr-4": { 110 | "PrettyDateTime\\": "src/" 111 | } 112 | }, 113 | "notification-url": "https://packagist.org/downloads/", 114 | "license": [ 115 | "MIT" 116 | ], 117 | "authors": [ 118 | { 119 | "name": "Daniel St. Jules", 120 | "email": "danielst.jules@gmail.com", 121 | "homepage": "http://www.danielstjules.com" 122 | } 123 | ], 124 | "description": "Generates human-readable strings for PHP DateTime objects", 125 | "keywords": [ 126 | "date", 127 | "datetime", 128 | "human", 129 | "pretty", 130 | "readable", 131 | "string", 132 | "time" 133 | ], 134 | "time": "2014-12-16 01:27:21" 135 | }, 136 | { 137 | "name": "guzzlehttp/guzzle", 138 | "version": "6.2.2", 139 | "source": { 140 | "type": "git", 141 | "url": "https://github.com/guzzle/guzzle.git", 142 | "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" 143 | }, 144 | "dist": { 145 | "type": "zip", 146 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", 147 | "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", 148 | "shasum": "" 149 | }, 150 | "require": { 151 | "guzzlehttp/promises": "^1.0", 152 | "guzzlehttp/psr7": "^1.3.1", 153 | "php": ">=5.5" 154 | }, 155 | "require-dev": { 156 | "ext-curl": "*", 157 | "phpunit/phpunit": "^4.0", 158 | "psr/log": "^1.0" 159 | }, 160 | "type": "library", 161 | "extra": { 162 | "branch-alias": { 163 | "dev-master": "6.2-dev" 164 | } 165 | }, 166 | "autoload": { 167 | "files": [ 168 | "src/functions_include.php" 169 | ], 170 | "psr-4": { 171 | "GuzzleHttp\\": "src/" 172 | } 173 | }, 174 | "notification-url": "https://packagist.org/downloads/", 175 | "license": [ 176 | "MIT" 177 | ], 178 | "authors": [ 179 | { 180 | "name": "Michael Dowling", 181 | "email": "mtdowling@gmail.com", 182 | "homepage": "https://github.com/mtdowling" 183 | } 184 | ], 185 | "description": "Guzzle is a PHP HTTP client library", 186 | "homepage": "http://guzzlephp.org/", 187 | "keywords": [ 188 | "client", 189 | "curl", 190 | "framework", 191 | "http", 192 | "http client", 193 | "rest", 194 | "web service" 195 | ], 196 | "time": "2016-10-08 15:01:37" 197 | }, 198 | { 199 | "name": "guzzlehttp/promises", 200 | "version": "1.3.0", 201 | "source": { 202 | "type": "git", 203 | "url": "https://github.com/guzzle/promises.git", 204 | "reference": "2693c101803ca78b27972d84081d027fca790a1e" 205 | }, 206 | "dist": { 207 | "type": "zip", 208 | "url": "https://api.github.com/repos/guzzle/promises/zipball/2693c101803ca78b27972d84081d027fca790a1e", 209 | "reference": "2693c101803ca78b27972d84081d027fca790a1e", 210 | "shasum": "" 211 | }, 212 | "require": { 213 | "php": ">=5.5.0" 214 | }, 215 | "require-dev": { 216 | "phpunit/phpunit": "~4.0" 217 | }, 218 | "type": "library", 219 | "extra": { 220 | "branch-alias": { 221 | "dev-master": "1.0-dev" 222 | } 223 | }, 224 | "autoload": { 225 | "psr-4": { 226 | "GuzzleHttp\\Promise\\": "src/" 227 | }, 228 | "files": [ 229 | "src/functions_include.php" 230 | ] 231 | }, 232 | "notification-url": "https://packagist.org/downloads/", 233 | "license": [ 234 | "MIT" 235 | ], 236 | "authors": [ 237 | { 238 | "name": "Michael Dowling", 239 | "email": "mtdowling@gmail.com", 240 | "homepage": "https://github.com/mtdowling" 241 | } 242 | ], 243 | "description": "Guzzle promises library", 244 | "keywords": [ 245 | "promise" 246 | ], 247 | "time": "2016-11-18 17:47:58" 248 | }, 249 | { 250 | "name": "guzzlehttp/psr7", 251 | "version": "1.3.1", 252 | "source": { 253 | "type": "git", 254 | "url": "https://github.com/guzzle/psr7.git", 255 | "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" 256 | }, 257 | "dist": { 258 | "type": "zip", 259 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", 260 | "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", 261 | "shasum": "" 262 | }, 263 | "require": { 264 | "php": ">=5.4.0", 265 | "psr/http-message": "~1.0" 266 | }, 267 | "provide": { 268 | "psr/http-message-implementation": "1.0" 269 | }, 270 | "require-dev": { 271 | "phpunit/phpunit": "~4.0" 272 | }, 273 | "type": "library", 274 | "extra": { 275 | "branch-alias": { 276 | "dev-master": "1.4-dev" 277 | } 278 | }, 279 | "autoload": { 280 | "psr-4": { 281 | "GuzzleHttp\\Psr7\\": "src/" 282 | }, 283 | "files": [ 284 | "src/functions_include.php" 285 | ] 286 | }, 287 | "notification-url": "https://packagist.org/downloads/", 288 | "license": [ 289 | "MIT" 290 | ], 291 | "authors": [ 292 | { 293 | "name": "Michael Dowling", 294 | "email": "mtdowling@gmail.com", 295 | "homepage": "https://github.com/mtdowling" 296 | } 297 | ], 298 | "description": "PSR-7 message implementation", 299 | "keywords": [ 300 | "http", 301 | "message", 302 | "stream", 303 | "uri" 304 | ], 305 | "time": "2016-06-24 23:00:38" 306 | }, 307 | { 308 | "name": "mtdowling/jmespath.php", 309 | "version": "2.3.0", 310 | "source": { 311 | "type": "git", 312 | "url": "https://github.com/jmespath/jmespath.php.git", 313 | "reference": "192f93e43c2c97acde7694993ab171b3de284093" 314 | }, 315 | "dist": { 316 | "type": "zip", 317 | "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/192f93e43c2c97acde7694993ab171b3de284093", 318 | "reference": "192f93e43c2c97acde7694993ab171b3de284093", 319 | "shasum": "" 320 | }, 321 | "require": { 322 | "php": ">=5.4.0" 323 | }, 324 | "require-dev": { 325 | "phpunit/phpunit": "~4.0" 326 | }, 327 | "bin": [ 328 | "bin/jp.php" 329 | ], 330 | "type": "library", 331 | "extra": { 332 | "branch-alias": { 333 | "dev-master": "2.0-dev" 334 | } 335 | }, 336 | "autoload": { 337 | "psr-4": { 338 | "JmesPath\\": "src/" 339 | }, 340 | "files": [ 341 | "src/JmesPath.php" 342 | ] 343 | }, 344 | "notification-url": "https://packagist.org/downloads/", 345 | "license": [ 346 | "MIT" 347 | ], 348 | "authors": [ 349 | { 350 | "name": "Michael Dowling", 351 | "email": "mtdowling@gmail.com", 352 | "homepage": "https://github.com/mtdowling" 353 | } 354 | ], 355 | "description": "Declaratively specify how to extract elements from a JSON document", 356 | "keywords": [ 357 | "json", 358 | "jsonpath" 359 | ], 360 | "time": "2016-01-05 18:25:05" 361 | }, 362 | { 363 | "name": "psr/http-message", 364 | "version": "1.0.1", 365 | "source": { 366 | "type": "git", 367 | "url": "https://github.com/php-fig/http-message.git", 368 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 369 | }, 370 | "dist": { 371 | "type": "zip", 372 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 373 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 374 | "shasum": "" 375 | }, 376 | "require": { 377 | "php": ">=5.3.0" 378 | }, 379 | "type": "library", 380 | "extra": { 381 | "branch-alias": { 382 | "dev-master": "1.0.x-dev" 383 | } 384 | }, 385 | "autoload": { 386 | "psr-4": { 387 | "Psr\\Http\\Message\\": "src/" 388 | } 389 | }, 390 | "notification-url": "https://packagist.org/downloads/", 391 | "license": [ 392 | "MIT" 393 | ], 394 | "authors": [ 395 | { 396 | "name": "PHP-FIG", 397 | "homepage": "http://www.php-fig.org/" 398 | } 399 | ], 400 | "description": "Common interface for HTTP messages", 401 | "homepage": "https://github.com/php-fig/http-message", 402 | "keywords": [ 403 | "http", 404 | "http-message", 405 | "psr", 406 | "psr-7", 407 | "request", 408 | "response" 409 | ], 410 | "time": "2016-08-06 14:39:51" 411 | }, 412 | { 413 | "name": "qiniu/php-sdk", 414 | "version": "v7.1.3", 415 | "source": { 416 | "type": "git", 417 | "url": "https://github.com/qiniu/php-sdk.git", 418 | "reference": "b91653485e36b4797d7a302cc86c49695e47a642" 419 | }, 420 | "dist": { 421 | "type": "zip", 422 | "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/b91653485e36b4797d7a302cc86c49695e47a642", 423 | "reference": "b91653485e36b4797d7a302cc86c49695e47a642", 424 | "shasum": "" 425 | }, 426 | "require": { 427 | "php": ">=5.3.3" 428 | }, 429 | "require-dev": { 430 | "phpunit/phpunit": "~4.0", 431 | "squizlabs/php_codesniffer": "~2.3" 432 | }, 433 | "type": "library", 434 | "autoload": { 435 | "psr-4": { 436 | "Qiniu\\": "src/Qiniu" 437 | }, 438 | "files": [ 439 | "src/Qiniu/functions.php" 440 | ] 441 | }, 442 | "notification-url": "https://packagist.org/downloads/", 443 | "license": [ 444 | "MIT" 445 | ], 446 | "authors": [ 447 | { 448 | "name": "Qiniu", 449 | "email": "sdk@qiniu.com", 450 | "homepage": "http://www.qiniu.com" 451 | } 452 | ], 453 | "description": "Qiniu Resource (Cloud) Storage SDK for PHP", 454 | "homepage": "http://developer.qiniu.com/", 455 | "keywords": [ 456 | "cloud", 457 | "qiniu", 458 | "sdk", 459 | "storage" 460 | ], 461 | "time": "2016-11-18 02:57:31" 462 | }, 463 | { 464 | "name": "upyun/sdk", 465 | "version": "2.2.0", 466 | "source": { 467 | "type": "git", 468 | "url": "https://github.com/upyun/php-sdk.git", 469 | "reference": "e32c453668403da5e9f77963fce348af46c9f34c" 470 | }, 471 | "dist": { 472 | "type": "zip", 473 | "url": "https://api.github.com/repos/upyun/php-sdk/zipball/e32c453668403da5e9f77963fce348af46c9f34c", 474 | "reference": "e32c453668403da5e9f77963fce348af46c9f34c", 475 | "shasum": "" 476 | }, 477 | "require": { 478 | "ext-curl": "*", 479 | "php": ">=5.3.0" 480 | }, 481 | "require-dev": { 482 | "phpunit/phpunit": "~4.0" 483 | }, 484 | "type": "library", 485 | "autoload": { 486 | "files": [ 487 | "upyun.class.php" 488 | ] 489 | }, 490 | "notification-url": "https://packagist.org/downloads/", 491 | "license": [ 492 | "MIT" 493 | ], 494 | "authors": [ 495 | { 496 | "name": "totoleo", 497 | "email": "totoleo@163.com" 498 | }, 499 | { 500 | "name": "lfeng", 501 | "email": "bonevv@gmail.com" 502 | }, 503 | { 504 | "name": "lvtongda", 505 | "email": "riyao.lyu@gmail.com" 506 | }, 507 | { 508 | "name": "sabakugaara", 509 | "email": "senellise@gmail.com" 510 | } 511 | ], 512 | "description": "UPYUN sdk for php", 513 | "homepage": "https://github.com/upyun/php-sdk/", 514 | "keywords": [ 515 | "sdk", 516 | "upyun" 517 | ], 518 | "time": "2016-05-14 03:52:23" 519 | } 520 | ], 521 | "packages-dev": [], 522 | "aliases": [], 523 | "minimum-stability": "stable", 524 | "stability-flags": { 525 | "danielstjules/php-pretty-datetime": 20 526 | }, 527 | "prefer-stable": false, 528 | "prefer-lowest": false, 529 | "platform": { 530 | "php": ">=5.3" 531 | }, 532 | "platform-dev": [] 533 | } 534 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iammapping/alfred-uploader/30cd0e5fa5d97808a292e33690a9b88ad354061a/icon.png -------------------------------------------------------------------------------- /src/Helper.php: -------------------------------------------------------------------------------- 1 | $value); 22 | } 23 | 24 | self::$env = array_merge(self::$env, $name); 25 | } 26 | 27 | public static function dirname() { 28 | return date('Y/m/d'); 29 | } 30 | 31 | public static function filename($rawname) { 32 | $info = pathinfo($rawname); 33 | $useRawName = self::getenv('USE_RAW_FILENAME', false); 34 | if (!$useRawName) { 35 | $rand = ''; 36 | $i = 8; 37 | while ($i--) { 38 | $rand .= rand(0, 9); 39 | } 40 | return time() . $rand . '.' . $info['extension']; 41 | } else { 42 | return preg_replace('/(\s|[?#%:])+/', '_', $info['filename']) . '.' . $info['extension']; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/History.php: -------------------------------------------------------------------------------- 1 | 0) { 42 | $saved = fread($handle, filesize(self::historyFile)); 43 | if (!empty($saved)) { 44 | $saved = json_decode($saved, true); 45 | } 46 | } 47 | 48 | // sort history by time desc 49 | $saved = array_merge($saved, $items); 50 | uasort($saved, function($a, $b) { 51 | return $b['time'] - $a['time']; 52 | }); 53 | 54 | // just keep history items below UPLOAD_HISTORY_COUNT 55 | $saved = array_slice($saved, 0, Helper::getenv('UPLOAD_HISTORY_COUNT', 50)); 56 | 57 | ftruncate($handle, 0); 58 | rewind($handle); 59 | fwrite($handle, json_encode($saved)); 60 | flock($handle, LOCK_UN); 61 | } else { 62 | echo "Could not Lock File!"; 63 | } 64 | 65 | fclose($handle); 66 | } 67 | } -------------------------------------------------------------------------------- /src/Provider/ProviderInterface.php: -------------------------------------------------------------------------------- 1 | bucket = Helper::getenv('QINIU_BUCKET'); 23 | $this->accessKey = Helper::getenv('QINIU_ACCESS_KEY'); 24 | $this->secretKey = Helper::getenv('QINIU_SECRET_KEY'); 25 | $this->baseurl = Helper::getenv('QINIU_BASEURL'); 26 | 27 | $auth = new Auth($this->accessKey, $this->secretKey); 28 | $this->token = $auth->uploadToken($this->bucket); 29 | 30 | $this->qiniu = new UploadManager(); 31 | } 32 | 33 | public function upload($file) { 34 | $dirname = Helper::dirname(); 35 | $filename = Helper::filename($file); 36 | 37 | list($ret, $err) = $this->qiniu->putFile($this->token, $dirname . '/' . $filename, $file); 38 | if ($err !== null) { 39 | throw new \Exception($err->message()); 40 | } 41 | 42 | return $this->baseurl . '/' . $dirname . '/' . $filename; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Provider/S3Provider.php: -------------------------------------------------------------------------------- 1 | bucket = Helper::getenv('S3_BUCKET'); 24 | $this->accessKey = Helper::getenv('S3_ACCESS_KEY'); 25 | $this->secretKey = Helper::getenv('S3_SECRET_KEY'); 26 | $this->region = Helper::getenv('S3_REGION'); 27 | $this->baseurl = Helper::getenv('S3_BASEURL'); 28 | 29 | $credentials = new Credentials($this->accessKey, $this->secretKey); 30 | $this->s3 = S3Client::factory(array( 31 | 'version' => 'latest', 32 | 'region' => $this->region, 33 | 'credentials' => $credentials 34 | )); 35 | } 36 | 37 | public function upload($file) { 38 | $dirname = Helper::dirname(); 39 | $filename = Helper::filename($file); 40 | 41 | $ret = $this->s3->putObject([ 42 | 'Bucket' => $this->bucket, 43 | 'Key' => $dirname . '/' . $filename, 44 | 'Body' => fopen($file, 'r'), 45 | 'ACL' => 'public-read', 46 | ]); 47 | 48 | if ($this->baseurl) { 49 | return $this->baseurl . '/' . $dirname . '/' . $filename; 50 | } else { 51 | return $ret->get('ObjectURL'); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Provider/UpyunProvider.php: -------------------------------------------------------------------------------- 1 | bucket = Helper::getenv('UPYUN_BUCKET'); 23 | $this->user = Helper::getenv('UPYUN_USER'); 24 | $this->pwd = Helper::getenv('UPYUN_PWD'); 25 | $this->useStream = Helper::getenv('UPYUN_USE_STREAM', false); 26 | $this->baseurl = Helper::getenv('UPYUN_BASEURL', 'https://' . $this->bucket . '.b0.upaiyun.com'); 27 | 28 | $this->upyun = new UpYun($this->bucket, $this->user, $this->pwd); 29 | } 30 | 31 | public function upload($file) { 32 | // key start with '/' 33 | $dirname = '/' . Helper::dirname(); 34 | $filename = Helper::filename($file); 35 | if ($this->useStream) { 36 | $fh = fopen($file, 'r'); 37 | $this->upyun->writeFile($dirname . '/' . $filename, $fh, true); 38 | fclose($fh); 39 | } else { 40 | $this->upyun->writeFile($dirname . '/' . $filename, file_get_contents($file), true); 41 | } 42 | 43 | return $this->baseurl . $dirname . '/' . $filename; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Uploader.php: -------------------------------------------------------------------------------- 1 | files = $files; 16 | 17 | Helper::setenv($env); 18 | 19 | $provider = 'Upload\\Provider\\' . ucfirst(strtolower(Helper::getenv('UPLOAD_PROVIDER', 'upyun'))) . 'Provider'; 20 | if (!class_exists($provider)) { 21 | throw new \Exception("{$provider} is not defined"); 22 | } 23 | 24 | $this->provider = new $provider(); 25 | } 26 | 27 | public function upload() { 28 | $res = array(); 29 | foreach ($this->files as $file) { 30 | $url = $this->provider->upload($file); 31 | $res[] = array( 32 | 'file' => $file, 33 | 'url' => $url, 34 | 'time' => time() 35 | ); 36 | } 37 | 38 | return $res; 39 | } 40 | } --------------------------------------------------------------------------------