2016-08-14 106 views
7

在Heroku上部署的Django項目中,我曾經通過boto將文件上傳到Google雲存儲。但是,最近我不得不上傳大文件,這會導致Heroku超時。Django,Heroku,boto:直接將文件上傳到Google雲存儲

我下面Heroku的文檔有關direct file upload to S3,和定製如下:

的Python:

conn = boto.connect_gs(gs_access_key_id=GS_ACCESS_KEY, 
         gs_secret_access_key=GS_SECRET_KEY) 
presignedUrl = conn.generate_url(expires_in=3600, method='PUT', bucket=<bucketName>, key=<fileName>, force_http=True) 

JS:

url = 'https://<bucketName>.storage.googleapis.com/<fileName>?Signature=...&Expires=1471451569&GoogleAccessId=...'; // "presignUrl" 

postData = new FormData(); 
postData.append(...); 
... 

$.ajax({ 
    url: url, 
    type: 'PUT', 
    data: postData, 
    processData: false, 
    contentType: false, 
}); 

,我得到了以下錯誤消息:

XMLHttpRequest cannot load http:/... Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. 

編輯:

gsutil cors get gs://<bucketName>輸出:

[{"maxAgeSeconds": 3600, "method": ["GET", "POST", "HEAD", "DELETE", "PUT"], "origin": ["*"], "responseHeader": ["Content-Type"]}] 

看來CORS是OK。那麼,我該如何解決這個問題呢?謝謝。

編輯2:

的OPTION請求的從Firefox頭:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Encoding: gzip, deflate 
Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.5,en;q=0.3 
Access-Control-Request-Method: PUT 
Connection: keep-alive 
Host: <bucketName>.storage.googleapis.com 
Origin: http://localhost:8000 
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:48.0) Gecko/20100101 Firefox/48.0 

從鉻的OPTION請求的報頭:

Accept:*/* 
Accept-Encoding:gzip, deflate, sdch 
Accept-Language:zh-TW,zh;q=0.8,en;q=0.6,en-US;q=0.4,zh-CN;q=0.2 
Access-Control-Request-Headers: 
Access-Control-Request-Method:PUT 
Connection:keep-alive 
Host:directupload.storage.googleapis.com 
Origin:http://localhost:8000 
Referer:http://localhost:8000/ 
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 
X-Client-Data:CIe2yQEIprbJAQjznMoB 
+0

這將有助於如果您包括了預檢頭(OPTIONS)請求和響應aders。特別是請求上的ACCESS-CONTROL-REQUEST- *和ORIGIN頭以及響應上的ACCESS-CONTROL- *頭。 –

+0

你會詳細解釋一下,因爲我不知道如何包含這些數據。非常感謝。 –

+0

最簡單的方法是使用瀏覽器的開發工具(通常是F12,但並非總是如此,例如OS X上的Chrome使用OPTION-COMMAND-i)。開發人員工具應該有一個網絡選項卡。確保它正在捕獲流量,每個瀏覽器都有點不同。然後繼續併發出您的AJAX請求,瀏覽器應捕獲傳出請求和響應。如果您選擇適當的請求,您應該能夠看到關於瀏覽器請求和服務器響應的大量信息。 –

回答

3

頭問題不是從未來您的應用,我認爲它來自雲存儲分區。設置api時我遇到同樣的問題,您發佈的資源缺少標題。

https://cloud.google.com/storage/docs/cross-origin

雖然爲了防止惡意的行爲非常有用,這個安全措施也阻止已知的起點之間有用的和合法的相互作用。例如,example.appspot.com上Google App Engine託管的頁面上的腳本可能需要使用存儲在雲存儲存儲桶中的靜態資源(example.storage.googleapis.com)。但是,因爲從瀏覽器的角度來看,這兩個來源是不同的,所以瀏覽器將不允許example.appspot.com的腳本使用XMLHttpRequest從example.storage.googleapis.com獲取資源,因爲所提取的資源來自不同的起源。

所以它看起來像你需要配置存儲桶以允許cors請求。谷歌文檔顯示下面的代碼從谷歌cli運行。

https://cloud.google.com/storage/docs/cross-origin#Configuring-CORS-on-a-Bucket

gsutil cors set cors-json-file.json gs://example 

[ 
    { 
     "origin": ["http://mysite.heroku.com"], 
     "responseHeader": ["Content-Type"], 
     "method": ["GET", "HEAD", "DELETE", "PUT"], 
     "maxAgeSeconds": 3600 
    } 
] 

這樣可以讓你得到,上傳和刪除的內容。希望有所幫助。

+0

仍然無法正常工作。我在我的問題中添加了編輯部分。 –

1

根據編輯2中的信息,請求出了問題。預檢(OPTIONS)請求包括標題ACCESS-CONTROL-REQUEST-HEADER。這不是有效的CORS標題。正確的標題是ACCESS-CONTROL-REQUEST-HEADERS,最後注意'S'。

即使標頭正確,它也不應請求對access-control-allow-origin標頭的授權。 ACCESS-CONTROL-ALLOW-ORIGIN不是從客戶端發送的標頭。當服務器收到預檢請求時,它將自動發送到服務器到客戶端的響應中。除非客戶端/瀏覽器獲得ACCESS-CONTROL-ALLOW-ORIGIN標頭授權瀏覽器文檔當前來源於預檢請求中的跨源服務器,否則客戶端/瀏覽器不會允許跨源PUT請求。

錯誤標題的出現似乎與您收到的錯誤響應很好地相關。但是,它看起來像頭可能不是在您的原始代碼中,它看起來像稍後添加它(根據您的意見)。一定要把這個頭配置出來,這肯定是不正確的。

所以我對這個標題來自哪裏有點困惑,但我認爲它是你的問題的根源。

看起來您正在使用jQuery來製作AJAX PUT請求。我真的可以建議的是確保你沒有在你的JS代碼中的某個地方調用$ .ajaxSetup()來配置錯誤的頭文件。

+0

謝謝,喬迪。 'ACCESS-CONTROL-REQUEST-HEADER'(沒有'S')只是我錯誤的複製和粘貼錯誤。對於那個很抱歉。是的,我在ajax PUT請求中添加了這樣一個頭文件;所以,我錯了。忽略編輯2,你會提供更具體的建議嗎?我的所有代碼都在問題中(Python和JS),而且我沒有執行任何'$ .ajaxSetup()'。 –

+0

嘗試更改桶上的CORS配置以打開responseHeader爲*。 –

+0

示例:'[{「maxAgeSeconds」:3600,「method」:[「GET」,「POST」,「HEAD」,「DELETE」,「PUT」],「origin」:[「*」],「responseHeader 「:[」*「]}]' –

0

經過這麼多的試驗和錯誤,我想出了以下內容。程序運行,但是,有時/一些上傳的圖像不可見;其他時間他們都可以。我不知道爲什麼會發生這種情況。

我想徵求更多的想法,爲什麼文件上傳是好的,但一些圖像已損壞。

gsutil命令:

內容的
gsutil cors set cors.json gs://<bucketName> 
gsutil defacl ch -u allUsers:R gs://<bucketName> 

cors.json文件:

[ 
    { 
     "origin": ["*"], 
     "responseHeader": ["Content-Type"], 
     "method": ["GET", "POST", "HEAD", "DELETE", "PUT"], 
     "maxAgeSeconds": 3600 
    } 
] 

HTML:

<p id=status>Choose your avatar:</p> 
<input id=fileInput type=file> 

的JavaScript:

$(document).on('change', '#fileInput', function() { 
    var $this = $(this); 
    var file = $this[0].files[0]; 

    $.ajax({ 
    url: 'upload/sign/?fileName=' + file.name + '&contentType=' + file.type, 
    type: 'GET' 
    }) 
    .done(function(data) { 
    var response = JSON.parse(data); 
    uploadFile(file, response.presignedUrl, response.url, response.contentType) 
    }) 
    .fail(function() { 
    alert('Unable to obtain a signed URL.'); 
    }); 
}); 

function uploadFile(file, presignedUrl, url, contentType) { 
    var postData = new FormData(); 
    postData.append('file', file); 

    $.ajax({ 
    url: presignedUrl, 
    type: 'PUT', 
    data: postData, 
    headers: { 
     'Content-Type': contentType, 
    }, 
    processData: false, 
    contentType: false 
    }) 
    .done(function() { 
    alert('File upload successful'); 
    }) 
    .fail(function() { 
    alert('Unable to upload the file.'); 
    }); 
} 

Django的:

項目的urls.py

urlpatterns = [ 
    ... 
    url(r'upload/', include('upload.urls', namespace='upload')), 
] 

應用的urls.py

urlpatterns = [ 
    url(r'^$', views.upload, name='upload'), 
    url(r'^sign/', views.sign, name='sign'), 
] 

views.py:

def upload(request): 
    # ... render the template 


def sign(request): 
    fileName = request.GET.get('fileName') 
    contentType = request.GET.get('contentType') 
    conn = boto.connect_gs(gs_access_key_id=GS_ACCESS_KEY, 
          gs_secret_access_key=GS_SECRET_KEY) 
    presignedUrl = conn.generate_url(3600, 'PUT', GS_BUCKET_NAME, fileName, headers={'Content-Type':contentType}) 
    return HttpResponse(
     json.dumps({ 
      'presignedUrl': presignedUrl, 
      'url': GS_URL + fileName, 
      'contentType': contentType 
     }) 
    ) 
+0

經過多次試驗和錯誤之後,我發現上述代碼適用於pdf和純文本文件。只有圖像文件有問題。爲什麼? –

相關問題