2012-03-30 42 views
5

Tôi đang cố viết một ứng dụng cho phép người dùng tải tệp lên tài khoản Google Cloud Storage của mình. Để ngăn chặn việc ghi đè và thực hiện một số xử lý tùy chỉnh và đăng nhập vào bên tôi, tôi đang sử dụng máy chủ Node.js làm người trung gian để tải lên. Vì vậy, quá trình này là:Tệp POST của Node.js tới Máy chủ

  1. cập nhật tài khoản nộp để Node.js server
  2. Node.js máy chủ phân tích tập tin, kiểm tra loại tập tin, lưu trữ một số dữ liệu trong DB
  3. cập
  4. Node.js máy chủ tập tin để GCS
  5. Node.js máy chủ đáp ứng với yêu cầu của người dùng với một đường chuyền/thất bại xét

tôi nhận được một chút mất trên bước 3, chính xác làm thế nào để gửi tập tin đó để GCS. This question cung cấp một số thông tin chi tiết hữu ích, cũng như một ví dụ hay, nhưng tôi vẫn còn bối rối.

Tôi hiểu rằng tôi có thể mở ReadStream cho tệp tải lên tạm thời và đường dẫn đến đối tượng http.request(). Những gì tôi đang bối rối về là làm thế nào để tôi biểu thị trong yêu cầu POST của tôi rằng dữ liệu đường ống là biến số file. Theo số GCS API Docs, cần phải có biến số file và cần phải là biến cuối cùng.

Vì vậy, làm cách nào để chỉ định tên biến POST cho dữ liệu đường ống?

điểm thưởng nếu bạn có thể cho tôi biết làm thế nào để ống nó trực tiếp từ upload của người dùng của tôi, chứ không phải lưu trữ nó trong một tập tin tạm thời

Trả lời

4

Tôi tin rằng nếu bạn muốn làm POST, bạn phải sử dụng một tiêu đề. Và sau đó, trong cơ thể, write() một cái gì đó như thế này đối với từng lĩnh vực chuỗi (linebreaks nên \r\n):

--myboundary 
Content-Disposition: form-data; name="field_name" 

field_value 

và sau đó cho các tập tin bản thân, write() một cái gì đó như thế này để cơ thể:

--myboundary 
Content-Disposition: form-data; name="file"; filename="urlencoded_filename.jpg" 
Content-Type: image/jpeg 
Content-Transfer-Encoding: binary 

binary_file_data 

Các binary_file_data là nơi bạn sử dụng pipe():

var fileStream = fs.createReadStream("path/to/my/file.jpg"); 
fileStream.pipe(requestToGoogle, {end: false}); 
fileStream.on('end, function() { 
    req.end("--myboundary--\r\n\r\n"); 
}); 

Các {end: false} ngăn pipe() tự động đóng yêu cầu vì bạn cần phải viết thêm một đường biên sau khi bạn gửi xong tệp. Lưu ý thêm -- ở cuối ranh giới.

Bí quyết lớn là Google có thể yêu cầu tiêu đề content-length (rất có thể). Nếu đúng như vậy, bạn không thể truyền một POST từ người dùng của mình tới POST tới Google vì bạn sẽ không biết chắc chắn những gì mà content-length là cho đến khi bạn nhận được toàn bộ tệp.

Giá trị của tiêu đề content-length phải là một số duy nhất cho toàn bộ phần thân. Cách đơn giản để thực hiện việc này là gọi số Buffer.byteLength(body) trên toàn bộ cơ thể, nhưng điều đó sẽ trở nên xấu xí nhanh chóng nếu bạn có các tệp lớn và nó cũng sẽ tiêu diệt luồng.Một giải pháp thay thế sẽ là tính toán như sau:

var body_before_file = "..."; // string fields + boundary and metadata for the file 
var body_after_file = "--myboundary--\r\n\r\n"; 
var fs = require('fs'); 
fs.stat(local_path_to_file, function(err, file_info) { 
    var content_length = Buffer.byteLength(body_before_file) + 
      file_info.size + 
      Buffer.byteLength(body_after_file); 
    // create request to google, write content-length and other headers 
    // write() the body_before_file part, 
    // and then pipe the file and end the request like we did above 

Tuy nhiên, vẫn có thể tải xuống đĩa địa phương để xác định độ dài của tệp.

lựa chọn thay thế

... bây giờ, sau khi vượt qua tất cả điều đó, PUT có thể là bạn của bạn ở đây. Theo số https://developers.google.com/storage/docs/reference-methods#putobject, bạn có thể sử dụng tiêu đề transfer-encoding: chunked để bạn không cần tìm chiều dài tệp. Và, tôi tin rằng toàn bộ cơ thể của yêu cầu chỉ là tệp, vì vậy bạn có thể sử dụng pipe() và chỉ để cho nó kết thúc yêu cầu khi nó được thực hiện. Nếu bạn đang sử dụng https://github.com/felixge/node-formidable để xử lý video tải lên, thì bạn có thể làm điều gì đó như sau:

incomingForm.onPart = function(part) { 
    if (part.filename) { 
     var req = ... // create a PUT request to google and set the headers 
     part.pipe(req); 
    } else { 
     // let formidable handle all non-file parts 
     incomingForm.handlePart(part); 
    } 
} 
+0

Thật không may, Google yêu cầu nó là yêu cầu POST để làm những gì tôi đang cố gắng làm. Tuy nhiên, câu trả lời tuyệt vời, và tôi chắc chắn sẽ thực hiện các đề xuất bạn đã thực hiện. – jwegner

+0

Ngoài ra, bằng cách này, node.js cung cấp cho bạn khả năng [dừng đường ống từ việc đóng kết nối] (http://nodejs.org/api/stream.html#stream_stream_pipe_destination_options) – jwegner

+0

Điểm tốt, tôi quên mất điều đó. Tôi sẽ chỉnh sửa câu trả lời trong trường hợp bất kỳ ai khác bắt gặp nó. –

Các vấn đề liên quan