Bagaimana cara mengunduh file dengan Node.js (tanpa menggunakan perpustakaan pihak ketiga)?
443
Bagaimana cara mengunduh file dengan Node.js tanpa menggunakan perpustakaan pihak ketiga ?
Saya tidak butuh sesuatu yang istimewa. Saya hanya ingin mengunduh file dari URL yang diberikan, dan kemudian menyimpannya ke direktori yang diberikan.
"unduh file dengan node.js" - maksud Anda mengunggah ke server? atau mengambil file dari server jauh menggunakan server Anda? atau melayani file ke klien untuk diunduh dari server node.js Anda?
Joseph
66
"Saya hanya ingin mengunduh file dari url yang diberikan, dan kemudian menyimpannya ke direktori yang diberikan," tampaknya cukup jelas. :)
Michelle Tilley
34
Joseph membuat pernyataan yang salah bahwa semua proses simpul adalah proses server
lededje
1
@lededje Apa yang mencegah proses server mengunduh file dan menyimpannya ke direktori di server? Itu bisa dilakukan secara prefect.
Gherman
Jawaban:
598
Anda dapat membuat GETpermintaan HTTP dan responsemengirimkannya ke aliran file yang dapat ditulisi:
Jika Anda ingin mendukung pengumpulan informasi di baris perintah - seperti menentukan file target atau direktori, atau URL - lihat sesuatu seperti Komandan .
Aku punya konsol output berikut ketika aku berlari script ini: node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18) .
Anderson Green
Coba gunakan URL lain di http.gettelepon; mungkin http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(dan ganti file.pngdengan file.jpg).
Michelle Tilley
8
Apakah kode ini menutup file dengan benar ketika skrip berakhir atau apakah akan kehilangan data?
philk
2
@quantumpotato Lihatlah respons yang Anda dapatkan dari permintaan Anda
Michelle Tilley
6
Ini tergantung pada jenis url req jika Anda meminta httpsAnda harus menggunakan httpsselain itu akan menimbulkan kesalahan.
Krishnadas PC
523
Jangan lupa untuk menangani kesalahan! Kode berikut didasarkan pada jawaban Augusto Roman.
var http = require('http');var fs = require('fs');var download =function(url, dest, cb){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});}).on('error',function(err){// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result)if(cb) cb(err.message);});};
panggilan balik itu membingungkan saya. jika sekarang saya meminta download(), bagaimana saya melakukannya? Apa yang akan saya tempatkan sebagai cbargumen? Saya punya download('someURI', '/some/destination', cb)tetapi tidak mengerti apa yang harus dimasukkan ke dalam cb
Abdul
1
@ Abdul Anda menentukan panggilan balik dengan fungsi hanya jika Anda perlu melakukan sesuatu ketika file telah berhasil diambil.
CatalinBerta
65
Berbicara tentang penanganan kesalahan, lebih baik mendengarkan permintaan kesalahan juga. Saya bahkan akan memvalidasi dengan memeriksa kode respons. Di sini dianggap berhasil hanya untuk 200 kode respons, tetapi kode lain mungkin baik.
const fs = require('fs');const http = require('http');const download =(url, dest, cb)=>{const file = fs.createWriteStream(dest);const request = http.get(url,(response)=>{// check if response is successif(response.statusCode !==200){return cb('Response status was '+ response.statusCode);}
response.pipe(file);});// close() is async, call cb after close completes
file.on('finish',()=> file.close(cb));// check for request error too
request.on('error',(err)=>{
fs.unlink(dest);return cb(err.message);});
file.on('error',(err)=>{// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result) return cb(err.message);});};
Terlepas dari kesederhanaan relatif dari kode ini, saya akan menyarankan untuk menggunakan modul permintaan karena menangani lebih banyak protokol (halo HTTPS!) Yang tidak didukung secara native oleh http.
Itu akan dilakukan seperti itu:
const fs = require('fs');const request = require('request');const download =(url, dest, cb)=>{const file = fs.createWriteStream(dest);const sendReq = request.get(url);// verify response code
sendReq.on('response',(response)=>{if(response.statusCode !==200){return cb('Response status was '+ response.statusCode);}
sendReq.pipe(file);});// close() is async, call cb after close completes
file.on('finish',()=> file.close(cb));// check for request errors
sendReq.on('error',(err)=>{
fs.unlink(dest);return cb(err.message);});
file.on('error',(err)=>{// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result)return cb(err.message);});};
Modul permintaan hanya berfungsi langsung untuk HTTPs. Keren!
Thiago C. S Ventura
@ventura ya, btw, ada juga modul https asli yang sekarang dapat menangani koneksi yang aman.
Buzut
Ini lebih rentan kesalahan tanpa keraguan. Bagaimanapun, dalam hal apa pun di mana menggunakan modul permintaan adalah sebuah pilihan, saya akan menyarankannya karena tingkatnya jauh lebih tinggi dan dengan demikian, lebih mudah dan efisien.
Buzut
2
@Alex, tidak, ini adalah pesan kesalahan dan ada balasannya. Jadi jika response.statusCode !== 200cb on finishtidak akan pernah dipanggil.
Buzut
1
Terima kasih telah menunjukkan contoh menggunakan modul permintaan.
Pete Alvin
48
Jawaban gfxmonk memiliki ras data yang sangat ketat antara panggilan balik dan file.close()penyelesaian. file.close()sebenarnya membutuhkan callback yang dipanggil saat penutupan telah selesai. Jika tidak, penggunaan langsung file mungkin gagal (sangat jarang!).
Solusi lengkap adalah:
var http = require('http');var fs = require('fs');var download =function(url, dest, cb){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});});}
Tanpa menunggu acara selesai, skrip naif mungkin berakhir dengan file yang tidak lengkap. Tanpa menjadwalkan cbpanggilan balik melalui penutupan, Anda mungkin mendapatkan perlombaan antara mengakses file dan file yang sebenarnya sudah siap.
Dua komentar tentang ini: 1) itu mungkin harus menolak objek Kesalahan, bukan string, 2) fs.unlink diam-diam akan menelan kesalahan yang mungkin belum tentu menjadi apa yang ingin Anda lakukan
Richard Nienaber
1
Ini sangat bagus! Dan jika URL Anda menggunakan HTTPS, hanya pengganti const https = require("https");untukconst http = require("http");
Russ
15
Solusi dengan batas waktu, mencegah kebocoran memori:
Kode berikut didasarkan pada jawaban Brandon Tilley:
Anda dapat menambahkan batas waktu seperti yang saya lakukan di http.get. Kebocoran memori hanya jika file terlalu lama untuk diunduh.
A-312
13
bagi mereka yang datang mencari cara berbasis gaya es6 janji, saya kira itu akan menjadi seperti:
var http = require('http');var fs = require('fs');function pDownload(url, dest){var file = fs.createWriteStream(dest);returnnewPromise((resolve, reject)=>{var responseSent =false;// flag to make sure that response is sent only once.
http.get(url, response =>{
response.pipe(file);
file.on('finish',()=>{
file.close(()=>{if(responseSent)return;
responseSent =true;
resolve();});});}).on('error', err =>{if(responseSent)return;
responseSent =true;
reject(err);});});}//example
pDownload(url, fileLocation).then(()=> console.log('downloaded file no issues...')).catch( e => console.error('error while downloading', e));
responseSetBendera disebabkan, untuk beberapa alasan yang saya tidak punya waktu untuk menyelidiki, file saya untuk diunduh tidak lengkap. Tidak ada kesalahan muncul tetapi file .txt yang saya isi memiliki setengah dari baris yang perlu ada di sana. Menghapus logika untuk bendera memperbaikinya. Hanya ingin menunjukkan itu jika seseorang memiliki masalah dengan pendekatan tersebut. Tetap saja, +1
Milan Velebit
6
Kode Vince Yuan bagus tetapi tampaknya ada sesuatu yang salah.
function download(url, dest, callback){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(callback);// close() is async, call callback after close completes.});
file.on('error',function(err){
fs.unlink(dest);// Delete the file async. (But we don't check the result)if(callback)
callback(err.message);});});}
Sepertinya Permintaan telah ditinggalkan github.com/request/request/issues/3142"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
302 juga merupakan kode status HTTP untuk pengalihan URL, jadi Anda harus menggunakan ini [301.302] .indexOf (res.statusCode)! == -1 dalam pernyataan if
sidanmor
Pertanyaan-pertanyaan khusus untuk tidak termasuk mode pihak ketiga :)
David Gatti
3
Jika Anda menggunakan metode express use res.download (). jika tidak, gunakan modul fs.
Modul http tidak dapat https url, Anda akan mendapatkannya Protocol "https:" not supported.
Di sini saran saya:
Panggil alat sistem seperti wgetataucurl
gunakan beberapa alat seperti simpul-wget-janji yang juga sangat mudah digunakan.
var wget = require('node-wget-promise');
wget('http://nodejs.org/images/logo.svg');
Inilah cara lain untuk menanganinya tanpa ketergantungan pihak ketiga dan juga mencari pengalihan:
var download =function(url, dest, cb){var file = fs.createWriteStream(dest);
https.get(url,function(response){if([301,302].indexOf(response.statusCode)!==-1){
body =[];
download(response.headers.location, dest, cb);}
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});});}
...// part of importsconst{ download }= require('./utils/download');...// add this function wherever
download('https://imageurl.com','imagename.jpg',()=>{
console.log('done')});
Pembuangan kode pada umumnya tidak berguna dan mungkin diturunkan atau dihapus. Sebaiknya edit untuk setidaknya menjelaskan apa yang dilakukan kode untuk pengunjung masa depan.
Jawaban:
Anda dapat membuat
GET
permintaan HTTP danresponse
mengirimkannya ke aliran file yang dapat ditulisi:Jika Anda ingin mendukung pengumpulan informasi di baris perintah - seperti menentukan file target atau direktori, atau URL - lihat sesuatu seperti Komandan .
sumber
node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18)
.http.get
telepon; mungkinhttp://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
(dan gantifile.png
denganfile.jpg
).https
Anda harus menggunakanhttps
selain itu akan menimbulkan kesalahan.Jangan lupa untuk menangani kesalahan! Kode berikut didasarkan pada jawaban Augusto Roman.
sumber
download()
sendiripipe
bisa?Seperti yang dikatakan Michelle Tilley, tetapi dengan aliran kontrol yang sesuai:
Tanpa menunggu
finish
acara, skrip naif dapat berakhir dengan file yang tidak lengkap.Sunting: Terima kasih kepada @Augusto Roman karena menunjukkan yang
cb
harus diteruskanfile.close
, tidak dipanggil secara eksplisit.sumber
download()
, bagaimana saya melakukannya? Apa yang akan saya tempatkan sebagaicb
argumen? Saya punyadownload('someURI', '/some/destination', cb)
tetapi tidak mengerti apa yang harus dimasukkan ke dalam cbBerbicara tentang penanganan kesalahan, lebih baik mendengarkan permintaan kesalahan juga. Saya bahkan akan memvalidasi dengan memeriksa kode respons. Di sini dianggap berhasil hanya untuk 200 kode respons, tetapi kode lain mungkin baik.
Terlepas dari kesederhanaan relatif dari kode ini, saya akan menyarankan untuk menggunakan modul permintaan karena menangani lebih banyak protokol (halo HTTPS!) Yang tidak didukung secara native oleh
http
.Itu akan dilakukan seperti itu:
sumber
response.statusCode !== 200
cb onfinish
tidak akan pernah dipanggil.Jawaban gfxmonk memiliki ras data yang sangat ketat antara panggilan balik dan
file.close()
penyelesaian.file.close()
sebenarnya membutuhkan callback yang dipanggil saat penutupan telah selesai. Jika tidak, penggunaan langsung file mungkin gagal (sangat jarang!).Solusi lengkap adalah:
Tanpa menunggu acara selesai, skrip naif mungkin berakhir dengan file yang tidak lengkap. Tanpa menjadwalkan
cb
panggilan balik melalui penutupan, Anda mungkin mendapatkan perlombaan antara mengakses file dan file yang sebenarnya sudah siap.sumber
var request =
dihapus?Mungkin node.js telah berubah, tetapi tampaknya ada beberapa masalah dengan solusi lain (menggunakan simpul v8.1.2):
file.close()
difinish
acara tersebut. Per defaultfs.createWriteStream
disetel ke autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_optionsfile.close()
harus dipanggil kesalahan. Mungkin ini tidak diperlukan saat file dihapus (unlink()
), tetapi biasanya ini: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_optionsstatusCode !== 200
fs.unlink()
tanpa panggilan balik sudah usang (peringatan keluaran)dest
ada file; itu ditimpaDi bawah ini adalah solusi yang dimodifikasi (menggunakan ES6 dan janji-janji) yang menangani masalah ini.
sumber
const https = require("https");
untukconst http = require("http");
Solusi dengan batas waktu, mencegah kebocoran memori:
Kode berikut didasarkan pada jawaban Brandon Tilley:
Jangan membuat file saat Anda mendapatkan kesalahan, dan lebih suka menggunakan batas waktu untuk menutup permintaan Anda setelah X detik.
sumber
http.get("http://example.com/yourfile.html",function(){})
http.get
. Kebocoran memori hanya jika file terlalu lama untuk diunduh.bagi mereka yang datang mencari cara berbasis gaya es6 janji, saya kira itu akan menjadi seperti:
sumber
responseSet
Bendera disebabkan, untuk beberapa alasan yang saya tidak punya waktu untuk menyelidiki, file saya untuk diunduh tidak lengkap. Tidak ada kesalahan muncul tetapi file .txt yang saya isi memiliki setengah dari baris yang perlu ada di sana. Menghapus logika untuk bendera memperbaikinya. Hanya ingin menunjukkan itu jika seseorang memiliki masalah dengan pendekatan tersebut. Tetap saja, +1Kode Vince Yuan bagus tetapi tampaknya ada sesuatu yang salah.
sumber
Saya lebih suka request () karena Anda bisa menggunakan http dan https.
sumber
"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
sumber
Hai, saya pikir Anda dapat menggunakan modul child_process dan perintah curl.
Selain itu, ketika Anda ingin mengunduh 、 banyak file besar, Anda dapat menggunakan modul cluster untuk menggunakan lebih banyak core cpu.
sumber
Anda dapat menggunakan https://github.com/douzi8/ajax-request#download
sumber
ajax-request
itu bukan perpustakaan pihak ketiga?Unduh menggunakan janji, yang menyelesaikan aliran yang dapat dibaca. masukkan logika ekstra untuk menangani pengalihan.
sumber
Jika Anda menggunakan metode express use res.download (). jika tidak, gunakan modul fs.
(atau)
sumber
Dari jawaban saya untuk "Apa perbedaan antara .pipe dan .pipeline pada stream" .
sumber
Path: tipe img: jpg random uniqid
sumber
Tanpa perpustakaan itu bisa buggy hanya untuk menunjukkan. Berikut ini beberapa di antaranya:
Protocol "https:" not supported.
Di sini saran saya:
wget
ataucurl
var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');
sumber
sumber
Anda dapat mencoba menggunakan
res.redirect
url unduhan file https, dan kemudian akan mengunduh file tersebut.Suka:
res.redirect('https//static.file.com/file.txt');
sumber
sumber
Inilah cara lain untuk menanganinya tanpa ketergantungan pihak ketiga dan juga mencari pengalihan:
sumber
download.js (yaitu /project/utils/download.js)
app.js
sumber
Kita dapat menggunakan modul simpul unduhan dan sangat sederhana, silakan lihat di bawah ini https://www.npmjs.com/package/download
sumber
sumber