Tải xuống hình ảnh với node.js [đã đóng]


169

Tôi đang cố gắng viết một tập lệnh để tải xuống hình ảnh bằng cách sử dụng node.js. Đây là những gì tôi có cho đến nay:

var maxLength = 10 // 10mb
var download = function(uri, callback) {
  http.request(uri)
    .on('response', function(res) {
      if (res.headers['content-length'] > maxLength*1024*1024) {
        callback(new Error('Image too large.'))
      } else if (!~[200, 304].indexOf(res.statusCode)) {
        callback(new Error('Received an invalid status code.'))
      } else if (!res.headers['content-type'].match(/image/)) {
        callback(new Error('Not an image.'))
      } else {
        var body = ''
        res.setEncoding('binary')
        res
          .on('error', function(err) {
            callback(err)
          })
          .on('data', function(chunk) {
            body += chunk
          })
          .on('end', function() {
            // What about Windows?!
            var path = '/tmp/' + Math.random().toString().split('.').pop()
            fs.writeFile(path, body, 'binary', function(err) {
              callback(err, path)
            })
          })
      }
    })
    .on('error', function(err) {
      callback(err)
    })
    .end();
}

Tôi, tuy nhiên, muốn làm cho điều này mạnh mẽ hơn:

  1. Có thư viện làm điều này và làm điều này tốt hơn?
  2. Có khả năng các tiêu đề phản hồi nói dối (về độ dài, về loại nội dung) không?
  3. Có bất kỳ mã trạng thái nào khác tôi nên quan tâm? Tôi có nên bận tâm với chuyển hướng?
  4. Tôi nghĩ rằng tôi đã đọc ở đâu đó rằng binarymã hóa sẽ bị phản đối. Tôi phải làm gì sau đó?
  5. Làm thế nào tôi có thể làm điều này để làm việc trên windows?
  6. Bất kỳ cách nào khác bạn có thể làm cho kịch bản này tốt hơn?

Tại sao: đối với một tính năng tương tự như imgur nơi người dùng có thể cung cấp cho tôi URL, tôi tải xuống hình ảnh đó và chỉnh lại hình ảnh theo nhiều kích cỡ.

Câu trả lời:


401

Tôi đề nghị sử dụng mô-đun yêu cầu . Tải xuống một tệp đơn giản như mã sau đây:

var fs = require('fs'),
    request = require('request');

var download = function(uri, filename, callback){
  request.head(uri, function(err, res, body){
    console.log('content-type:', res.headers['content-type']);
    console.log('content-length:', res.headers['content-length']);

    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
  });
};

download('https://www.google.com/images/srpr/logo3w.png', 'google.png', function(){
  console.log('done');
});

1
Mát mẻ! Có cách nào để kiểm tra kích thước và loại nội dung trước khi thực sự tải xuống không?
Jonathan Ong

2
Nó tải hình ảnh về đâu?
Gofilord

17
Không làm việc cho tôi (Hình ảnh bị hỏng
Darth

2
@Gofilord tải hình ảnh về thư mục gốc của bạn.
dang

1
Bạn có thể thay đổi vị trí của nơi họ được lưu? Nếu bạn muốn chúng trong một thư mục cụ thể?
AKL012

33

Tôi đã gặp vấn đề này vài ngày trước, vì một câu trả lời thuần túy của NodeJS tôi sẽ đề nghị sử dụng Stream để hợp nhất các khối lại với nhau.

var http = require('http'),                                                
    Stream = require('stream').Transform,                                  
    fs = require('fs');                                                    

var url = 'http://www.google.com/images/srpr/logo11w.png';                    

http.request(url, function(response) {                                        
  var data = new Stream();                                                    

  response.on('data', function(chunk) {                                       
    data.push(chunk);                                                         
  });                                                                         

  response.on('end', function() {                                             
    fs.writeFileSync('image.png', data.read());                               
  });                                                                         
}).end();

Các phiên bản Node mới nhất sẽ không hoạt động tốt với các chuỗi nhị phân, do đó, việc hợp nhất các chuỗi với các chuỗi không phải là ý tưởng hay khi làm việc với dữ liệu nhị phân.

* Chỉ cần cẩn thận khi sử dụng 'data.read ()', nó sẽ làm trống luồng cho hoạt động 'read ()' tiếp theo. Nếu bạn muốn sử dụng nó nhiều lần, hãy lưu trữ nó ở đâu đó.


7
Tại sao không truyền tải trực tiếp vào đĩa?
geon

có rất nhiều vấn đề với việc xâu chuỗi lại với nhau khi nó tạo ra một tệp bị hỏng, nhưng điều này đã làm điều đó
Shaho

27

Bạn có thể sử dụng Axios ( ứng dụng khách HTTP dựa trên lời hứa cho Node.js) để tải xuống hình ảnh theo thứ tự bạn chọn trong môi trường không đồng bộ :

npm i axios

Sau đó, bạn có thể sử dụng ví dụ cơ bản sau để bắt đầu tải xuống hình ảnh:

const fs = require('fs');
const axios = require('axios');

/* ============================================================
  Function: Download Image
============================================================ */

const download_image = (url, image_path) =>
  axios({
    url,
    responseType: 'stream',
  }).then(
    response =>
      new Promise((resolve, reject) => {
        response.data
          .pipe(fs.createWriteStream(image_path))
          .on('finish', () => resolve())
          .on('error', e => reject(e));
      }),
  );

/* ============================================================
  Download Images in Order
============================================================ */

(async () => {
  let example_image_1 = await download_image('https://example.com/test-1.png', 'example-1.png');

  console.log(example_image_1.status); // true
  console.log(example_image_1.error); // ''

  let example_image_2 = await download_image('https://example.com/does-not-exist.png', 'example-2.png');

  console.log(example_image_2.status); // false
  console.log(example_image_2.error); // 'Error: Request failed with status code 404'

  let example_image_3 = await download_image('https://example.com/test-3.png', 'example-3.png');

  console.log(example_image_3.status); // true
  console.log(example_image_3.error); // ''
})();

2
Ví dụ tuyệt vời! Nhưng mã hầu như không đọc được, hãy thử kiểu chuẩn : D
camwhite

3
@camwhite Mình thích dấu chấm phẩy . ;)
Grant Miller

1
Bạn thực sự nên đính kèm các sự kiện 'kết thúc' và 'lỗi' vào luồng ghi, gói chúng trong một Lời hứa và trả lại lời hứa. Nếu không, bạn có thể thử truy cập một hình ảnh chưa được tải xuống hoàn toàn.
jwerre

Sẽ không chờ đợi để đảm bảo tải xuống hình ảnh hoàn toàn trước khi cố gắng truy cập? @jwerre
FabricioG

@jwerre @FovenioG Tôi đã cập nhật chức năng download_imageđể nắm bắt sự kiện 'kết thúc' và 'lỗi' cho lời hứa đã trả lại
Beeno Tung

10

Nếu bạn muốn tiến trình tải về, hãy thử điều này:

var fs = require('fs');
var request = require('request');
var progress = require('request-progress');

module.exports = function (uri, path, onProgress, onResponse, onError, onEnd) {
    progress(request(uri))
    .on('progress', onProgress)
    .on('response', onResponse)
    .on('error', onError)
    .on('end', onEnd)
    .pipe(fs.createWriteStream(path))
};

cách sử dụng:

  var download = require('../lib/download');
  download("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png", "~/download/logo.png", function (state) {
            console.log("progress", state);
        }, function (response) {
            console.log("status code", response.statusCode);
        }, function (error) {
            console.log("error", error);
        }, function () {
            console.log("done");
        });

lưu ý: bạn nên cài đặt cả hai mô-đun yêu cầu & tiến trình yêu cầu bằng cách sử dụng:

npm install request request-progress --save

2
Điều này làm việc tuyệt vời, nhưng muốn đề nghị thêm một statusCodekiểm tra. Một mã trạng thái 500 chẳng hạn, sẽ không đạt 'on("error", e). Bằng cách thêm on('response', (response) => console.error(response.statusCode))nó, nó hỗ trợ rất nhiều cho việc gỡ lỗi,
mateuscb

1
Bạn có thể chỉnh sửa câu trả lời của tôi :)
Fnamed Alnamrouti

4

Dựa trên những điều trên, nếu bất cứ ai cần xử lý lỗi trong luồng ghi / đọc, tôi đã sử dụng phiên bản này. Lưu ý stream.read()trong trường hợp có lỗi ghi, nó là bắt buộc để chúng ta có thể đọc xong và kích hoạt closetrên luồng đọc.

var download = function(uri, filename, callback){
  request.head(uri, function(err, res, body){
    if (err) callback(err, filename);
    else {
        var stream = request(uri);
        stream.pipe(
            fs.createWriteStream(filename)
                .on('error', function(err){
                    callback(error, filename);
                    stream.read();
                })
            )
        .on('close', function() {
            callback(null, filename);
        });
    }
  });
};

2
stream.read()dường như đã lỗi thời, gây ra lỗinot a function
uốn cong

4
var fs = require('fs'),
http = require('http'),
https = require('https');

var Stream = require('stream').Transform;

var downloadImageToUrl = (url, filename, callback) => {

    var client = http;
    if (url.toString().indexOf("https") === 0){
      client = https;
     }

    client.request(url, function(response) {                                        
      var data = new Stream();                                                    

      response.on('data', function(chunk) {                                       
         data.push(chunk);                                                         
      });                                                                         

      response.on('end', function() {                                             
         fs.writeFileSync(filename, data.read());                               
      });                                                                         
   }).end();
};

downloadImageToUrl('https://www.google.com/images/srpr/logo11w.png', 'public/uploads/users/abc.jpg');

1
chức năng của bạn không kích hoạt cuộc gọi lại
crockpotveggies

4

Đây là một phần mở rộng cho câu trả lời của Cezary. Nếu bạn muốn tải nó vào một thư mục cụ thể, hãy sử dụng cái này. Ngoài ra, sử dụng const thay vì var. Nó an toàn theo cách này.

const fs = require('fs');
const request = require('request');
var download = function(uri, filename, callback){
  request.head(uri, function(err, res, body){    
    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
  });
};

download('https://www.google.com/images/srpr/logo3w.png', './images/google.png', function(){
  console.log('done');
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.