第四章:minio的presigned URLs上傳文件

章節快捷訪問:

第一章:minio介紹與安裝

https://blog.csdn.net/hzw2312/article/details/106077729

第二章:minio單機版,使用客戶端備份文件

https://blog.csdn.net/hzw2312/article/details/106078150

第三章:minio的javaAPI

https://blog.csdn.net/hzw2312/article/details/106078390

第四章:minio的presigned URLs上傳文件

https://blog.csdn.net/hzw2312/article/details/106078604

--------------------------------------------------

 

當我們通過java的API上傳文件的時候就會發現,我們把java的API封裝了一下,提供了一個接口給其他應用調用,那麼整個的上傳流程就變成了“應用客戶端”-->“JavaAPI端”-->“minio服務端”。中間通過JavaAPI轉了一次。比如我們的“應用客戶端”是web瀏覽器的時候,能不能直接從瀏覽器上傳到“minio服務端”呢?答案是可以的,minio有提供JSSDK,我們可以通過JSAPI直接上傳到“minio服務端”。

JSSDK

參考官網的文檔:https://docs.min.io/cn/javascript-client-quickstart-guide.html

var Minio = require('minio')

// Instantiate the minio client with the endpoint
// and access keys as shown below.
var minioClient = new Minio.Client({
    endPoint: 'play.min.io',
    port: 9000,
    useSSL: true,
    accessKey: 'Q3AM3UQ867SPQQA43P2F',
    secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG'
});

// File that needs to be uploaded.
var file = '/tmp/photos-europe.tar'

// Make a bucket called europetrip.
minioClient.makeBucket('europetrip', 'us-east-1', function(err) {
    if (err) return console.log(err)

    console.log('Bucket created successfully in "us-east-1".')

    var metaData = {
        'Content-Type': 'application/octet-stream',
        'X-Amz-Meta-Testing': 1234,
        'example': 5678
    }
    // Using fPutObject API upload your file to the bucket europetrip.
    minioClient.fPutObject('europetrip', 'photos-europe.tar', file, metaData, function(err, etag) {
      if (err) return console.log(err)
      console.log('File uploaded successfully.')
    });
});

根據官網的文檔,我們可以看到,從js上傳文件需要將我們的accessKey跟secretKey暴露出去。這樣的話,別人拿到我們的key豈不是可以爲所欲爲了!

這樣是很危險的,所以我們不能這樣做。除非你是用這段腳本來寫node.js服務,把他當作服務端。但是這樣的話,豈不是又是通過“應用客戶端”-->“api端”-->“minio服務端”這樣的路徑來進行上傳了。

presigned URLs

minio並沒有像商用的七牛雲存儲那樣,可以通過臨時的token來上傳文件。但是,這個但是很重要啊,他提供了使用pre-signed URLs通過瀏覽器上傳文件。那麼這個pre-signed URLs是啥子?pre-signed URLs簡單來說就是預處理簽名的一個URL。就是url中包含了簽名信息,但是是經過加密的。而且還有時效性的。這樣一來就跟七牛雲的臨時token有異曲同工之處了。既保證了用最短的途徑能上傳文件,又保證了我們的上傳簽名信息不會被惡意利用。

官網的文檔:https://docs.min.io/cn/upload-files-from-browser-using-pre-signed-urls.html

這裏官網給出的例子是node.js的,我們使用Java該怎麼用呢!

首先在我們之前的MinioTemplate類中,加上支持方法:

/**
 * 獲得上傳的URL
 * @param bucketName
 * @param objectName
 * @param expires
 * @return
 * @throws IOException
 * @throws InvalidKeyException
 * @throws NoSuchAlgorithmException
 * @throws InsufficientDataException
 * @throws InvalidExpiresRangeException
 * @throws InternalException
 * @throws NoResponseException
 * @throws InvalidBucketNameException
 * @throws XmlPullParserException
 * @throws ErrorResponseException
 */
public String presignedPutObject(String bucketName, String objectName, Integer expires) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidExpiresRangeException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException {
    if(expires == null){
        // 7天
        return client.presignedPutObject(bucketName,objectName);
    }
    return client.presignedPutObject(bucketName,objectName,expires);
}

然後在我們之前的控制器中加上方法:

/**
 * 獲得上傳的URL
 * @param bucketName    文件桶名稱
 * @param objectName    文件對象名稱:(pdf/2019/0512/test.pdf)
 */
@ApiOperation(value = "獲得上傳的URL", response = ActionResult.class)
@GetMapping("/uploadUrl")
public ActionResult<Object> uploadUrl(
        @ApiParam(name = "bucketName",value = "文件桶名稱") String bucketName,
        @ApiParam(name = "objectName",value = "文件對象名稱:(pdf/2019/0512/test.pdf)") String objectName,
        @ApiParam(name = "expires", value ="鏈接有效時間(單位秒)") Integer expires)
        throws IOException, XmlPullParserException, NoSuchAlgorithmException, InvalidKeyException, InvalidPortException, ErrorResponseException, NoResponseException, InvalidBucketNameException, InsufficientDataException,  InternalException, InvalidExpiresRangeException {
    String url = template.presignedPutObject(bucketName,objectName,expires);
    return success(url,"獲取成功");
}

這樣js在上傳之前先調用我們的uploadUrl方法得到我們的上傳URL

在之前我們配置java的api鏈接的是51.78服務端,那麼我們先來看看裏面有什麼文件。

可以看到,只有一張圖片,下面我們調用java獲取上傳URL的方法:

可以看到我們調用方法,得到了一個帶有簽名信息的URL,下面我將使用這個url來進行文件的上傳:

需要注意一下,這裏是要發送一個PUT請求,還有上傳的是binary二進制文件流。

這裏上傳成功後,並沒有返回任何信息...我們打開51.78服務來看看

文件已經成功上傳了,在看看我們的51.80備份庫。

我們看到,也備份成功了。這一串路徑是因爲51.78文件服務的存儲地址就在這個路徑下面,所以備份的時候也將這些路徑帶來了。

這裏需要注意的是 ,我使用的是Postman測試工具來測試的,如果使用前端測試,需要注意跨域的問題,官網推薦使用的上傳js工具包是Fetch。我們看看官網的例子:

<input type="file" id="selector" multiple>
<button οnclick="upload()">Upload</button>

<div id="status">No uploads</div>

<script type="text/javascript">
    // `upload` iterates through all files selected and invokes a helper function called `retrieveNewURL`.
    function upload() {
        // Get selected files from the input element.
        var files = document.querySelector("#selector").files;
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            // 從服務器獲取一個URL
            retrieveNewURL(file, (file, url) => {
                // 上傳文件到服務器
                uploadFile(file, url);
            });
        }
    }

    // 發請求到Node.js server獲取上傳URL。
    // `retrieveNewURL` accepts the name of the current file and invokes the `/presignedUrl` endpoint to
    // generate a pre-signed URL for use in uploading that file: 
    function retrieveNewURL(file, cb) {
        fetch(`/presignedUrl?name=${file.name}`).then((response) => {
            response.text().then((url) => {
                cb(file, url);
            });
        }).catch((e) => {
            console.error(e);
        });
    }

    // 使用Fetch API來上傳文件到S3。
    // ``uploadFile` accepts the current filename and the pre-signed URL. It then uses `Fetch API`
    // to upload this file to S3 at `play.min.io:9000` using the URL:
    function uploadFile(file, url) {
        if (document.querySelector('#status').innerText === 'No uploads') {
            document.querySelector('#status').innerHTML = '';
        }
        fetch(url, {
            method: 'PUT',
            body: file
        }).then(() => {
            // If multiple files are uploaded, append upload status on the next line.
            document.querySelector('#status').innerHTML += `<br>Uploaded ${file.name}.`;
        }).catch((e) => {
            console.error(e);
        });
    }
</script>

好了,單機版本基本就結束了,如果後續還有啥要補充的,我會持續更新的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章