ফায়ারবেসের জন্য ক্লাউড ফাংশন সহ ফাইল আপলোড করা ফাইল থেকে ডাউনলোড URL পান


124

ফায়ারবেস ফাংশন ফর ফায়ারবেস স্টোরেজে কোনও ফাইল আপলোড করার পরে, আমি ফাইলটির ডাউনলোড ইউআরএল পেতে চাই।

আমার আছে এটা :

...

return bucket
    .upload(fromFilePath, {destination: toFilePath})
    .then((err, file) => {

        // Get the download url of file

    });

অবজেক্ট ফাইলে অনেকগুলি পরামিতি রয়েছে। এমনকি একটি নামকরণ mediaLink। তবে, আমি যদি এই লিঙ্কটি অ্যাক্সেস করার চেষ্টা করি তবে আমি এই ত্রুটিটি পেয়েছি:

বেনাম ব্যবহারকারীদের স্টোরেজ.অবজেক্টস নেই object অবজেক্টে অ্যাক্সেস নেই ...

কেউ আমাকে কীভাবে পাবলিক ডাউনলোড ইউরল পাবেন তা বলতে পারেন?

ধন্যবাদ


আরও দেখুন এই পোস্টে যা ফাংশনে ডেটা উপলব্ধ থেকে URL reconstructs।
কাটো

উত্তর:


133

আপনাকে @ গুগল-ক্লাউড / স্টোরেজ এনপিএম মডিউলটির মাধ্যমে getSided URL ব্যবহার করে একটি স্বাক্ষরিত URL তৈরি করতে হবে gene

উদাহরণ:

const gcs = require('@google-cloud/storage')({keyFilename: 'service-account.json'});
// ...
const bucket = gcs.bucket(bucket);
const file = bucket.file(fileName);
return file.getSignedUrl({
  action: 'read',
  expires: '03-09-2491'
}).then(signedUrls => {
  // signedUrls[0] contains the file's public URL
});

আপনি আরম্ভ করার প্রয়োজন হবে @google-cloud/storageসঙ্গে আপনার পরিষেবা অ্যাকাউন্ট পরিচয়পত্র হিসাবে আবেদন ডিফল্ট পরিচয়পত্র যথেষ্ট হবে না।

আপডেট : ক্লাউড স্টোরেজ এসডিকে এখন ফায়ারবেস অ্যাডমিন এসডিকে দিয়ে অ্যাক্সেস করা যেতে পারে, যা @ গুগল-ক্লাউড / স্টোরেজ জুড়ে মোড়ক হিসাবে কাজ করে । আপনি যদি হয় তবেই এটির একমাত্র উপায়:

  1. সাধারণত একটি দ্বিতীয়, অ-ডিফল্ট উদাহরণের মাধ্যমে একটি বিশেষ পরিষেবা অ্যাকাউন্ট দিয়ে এসডিকে সূচনা করুন।
  2. অথবা, কোনও পরিষেবা অ্যাকাউন্ট ছাড়াই, ডিফল্ট অ্যাপ ইঞ্জিন পরিষেবা অ্যাকাউন্টটিকে "সাইনব্লব" অনুমতি দিয়ে।

74
এটা অদ্ভুত. ফায়ারবেস অ্যান্ড্রয়েড, আইওএস এবং ওয়েব এসডিকে ব্যবহার করার সময় আমরা খুব সহজেই কোনও স্টোরেজ রেফারেন্স থেকে ডাউনলোড ইউআরটি পেতে পারি। অ্যাডমিনে থাকাকালীন কেন এত সহজ নয়? পিএস: জিসিএস শুরু করার জন্য প্রয়োজনীয় 'পরিষেবা-অ্যাকাউন্ট.জসন' কোথায় পাব?
ভ্যালেন্টিন

2
এর কারণ অ্যাডমিন-এসডিকে কোনও ক্লাউড স্টোরেজ সংযোজন নেই। আপনি এখানে আপনার অ্যাডমিন-এসডিকে পরিষেবা অ্যাকাউন্ট জসন পেতে পারেন এখানে কনসোল.ফায়ারবেস.google.com/project/_/settings/serviceaccounts/…
জেমস ড্যানিয়েল

18
এই পদ্ধতিতে উত্পন্ন URL টি হাস্যকরভাবে দীর্ঘ। @Martemorfosis প্রস্তাবিত পদ্ধতি দ্বারা উত্পন্ন URL টি আরও ভাল। এমন কোনও ফাংশন রয়েছে যা সেই ইউআরএল তৈরি করে? আমি যখন ফায়ারবেস-এসডিকে ব্যবহার করি তখন ভবিষ্যতের ব্যবহারের জন্য আমি ডাটাবেসে সংরক্ষণ করি। ফাংশন ডোমেনে একটি আয়না পদ্ধতি থাকা দরকার।
বোগাক

3
কীভাবে আমরা মোতায়েন করা ফাংশনগুলির সাথে সার্ভিস-অ্যাকাউন্ট.জসন আপলোড করতে পারি? আমি এটিকে ফাংশন ফোল্ডারে রাখার এবং প্যাকেজ.জসনে ফাইলের ক্ষেত্রে রেফারেন্স দেওয়ার চেষ্টা করেছি কিন্তু এটি মোতায়েন হচ্ছে না। ধন্যবাদ.
ডেভিড আরেস্টি

2
আমাদের কি যোগ করতে হবে actionএবং expires?
চাদ বিংহাম

82

আপলোডের ক্ষেত্রে ডাউনলোড টোকন কীভাবে নির্দিষ্ট করা যায় তার একটি উদাহরণ এখানে রয়েছে:

const UUID = require("uuid-v4");

const fbId = "<YOUR APP ID>";
const fbKeyFile = "./YOUR_AUTH_FIlE.json";
const gcs = require('@google-cloud/storage')({keyFilename: fbKeyFile});
const bucket = gcs.bucket(`${fbId}.appspot.com`);

var upload = (localFile, remoteFile) => {

  let uuid = UUID();

  return bucket.upload(localFile, {
        destination: remoteFile,
        uploadType: "media",
        metadata: {
          contentType: 'image/png',
          metadata: {
            firebaseStorageDownloadTokens: uuid
          }
        }
      })
      .then((data) => {

          let file = data[0];

          return Promise.resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(file.name) + "?alt=media&token=" + uuid);
      });
}

তারপরে কল করুন

upload(localPath, remotePath).then( downloadURL => {
    console.log(downloadURL);
  });

এখানে মূল বিষয়টি হ'ল বিকল্প সম্পত্তিটির metadataমধ্যে নেস্টেড একটি জিনিস রয়েছে metadatafirebaseStorageDownloadTokensএকটি uuid-v4 মান সেট করা ক্লাউড স্টোরেজকে তার সর্বজনীন প্রমাণ টোকেন হিসাবে এটি ব্যবহার করতে বলবে।

@ মার্টেমারফোসিসকে অনেক ধন্যবাদ


স্টোরেজে ইতিমধ্যে আপলোড করা কোনও ফাইলের জন্য আমি কীভাবে একটি বৈধ ইউআইডি টোকন পেতে পারি? এলোমেলোভাবে ইউইউডি উত্সাহ দেয়নি। কোন পয়েন্টার?
ডেরফাইজিও

3
@Martemorfosis পোস্টে উত্তরটি খুঁজে পেয়েছি। ইউইউডিটি অবজেক্টটি থেকে প্রাপ্ত করা যেতে পারে met // ...
ডেরফাইজিও

বালতি উদাহরণের জন্য আপনাকে ধন্যবাদ! আমি প্রায় 1 ঘন্টা ধরে বালতি এবং ফাইলের পদ্ধতিগুলির জন্য বিভিন্ন সংমিশ্রণ চেষ্টা করছিলাম :)
জেকার্লোসআর

1
আপনার উত্তরের জন্য ধন্যবাদ! আমার ক্ষেত্রে, আমি bucket.file (fileName) .CreateWriteStream দিয়ে আপলোড করছিলাম যা আপলোড শেষ হলে ডেটা ফেরত না, ফলস্বরূপ, আমি এনকোডিউআরআইসি কম্পোনেন্ট (ফাইলের নাম) ব্যবহার করে এনকোডিউআরআইকিউম্পোন্ট (ফাইলের নাম) ব্যবহার করি।
স্টানিসালু বুজুনকো

2
এটি সঠিক উত্তর হওয়া উচিত। এটি ফায়ারবেস এসডিকে (@ ডেভমাইক) দ্বারা উত্পন্ন একর মতো একটি URL এর ফলাফল দেয় এবং আমি দৃশ্যের পিছনে তারা ঠিক কী করে তা বাজি ধরছি।
স্যামুয়েল ই।

64

এই উত্তরটি গুগল / ফায়ারবেস ক্লাউড স্টোরেজে কোনও ফাইল আপলোড করার সময় ডাউনলোড URL পাওয়ার বিকল্পগুলির সংক্ষিপ্তসার করবে। ডাউনলোড URL টি তিন ধরণের রয়েছে:

  1. স্বাক্ষরিত ডাউনলোডের URL গুলি, যা অস্থায়ী এবং সুরক্ষা বৈশিষ্ট্যগুলি রয়েছে have
  2. টোকেন ডাউনলোড ইউআরএল, যা অবিরাম এবং সুরক্ষা বৈশিষ্ট্যগুলি রয়েছে
  3. সর্বজনীন ডাউনলোড ইউআরএল, যা অবিরাম এবং সুরক্ষার অভাব রয়েছে

টোকেন ডাউনলোড URL পাওয়ার জন্য তিনটি উপায় রয়েছে। অন্য দুটি ডাউনলোডের ইউআরএলগুলি এগুলি পাওয়ার কেবল একটি উপায় আছে।

ফায়ারবেস স্টোরেজ কনসোল থেকে

আপনি ফায়ারবেস স্টোরেজ কনসোল থেকে ডাউনলোড URL পেতে পারেন:

এখানে চিত্র বর্ণনা লিখুন

ডাউনলোড ইউআরএলটি দেখতে এমন দেখাচ্ছে:

https://firebasestorage.googleapis.com/v0/b/languagetwo-cd94d.appspot.com/o/Audio%2FEnglish%2FUnited_States-OED-0%2Fabout.mp3?alt=media&token=489c48b3-23fb-4270-bd85-0a328d2808e5

প্রথম অংশটি আপনার ফাইলের একটি মানক পথ। শেষে টোকেন হয়। এই ডাউনলোড URL টি স্থায়ী, অর্থাত্‍ এটির মেয়াদ শেষ হবে না, যদিও আপনি এটি প্রত্যাহার করতে পারেন।

getDownloadURL () ফ্রন্ট এন্ড থেকে

ডকুমেন্টেশন ব্যবহার আমাদের বলে getDownloadURL():

let url = await firebase.storage().ref('Audio/English/United_States-OED-' + i +'/' + $scope.word.word + ".mp3").getDownloadURL();

এটি একই ডাউনলোডের URL পেয়েছে যা আপনি আপনার ফায়ারবেস স্টোরেজ কনসোল থেকে পেতে পারেন। এই পদ্ধতিটি সহজ তবে আপনার অপ্রয়োজনীয় ডাটাবেস কাঠামোর জন্য আমার অ্যাপ্লিকেশনটিতে প্রায় 300 লাইন কোডের কোড যা আপনার ফাইলের পথটি জানতে হবে তা আপনার প্রয়োজন। আপনার ডাটাবেস জটিল হলে এটি একটি দুঃস্বপ্ন হতে পারে। এবং আপনি সামনের প্রান্ত থেকে ফাইলগুলি আপলোড করতে পারেন, তবে এটি আপনার অ্যাপ্লিকেশনটি ডাউনলোড করে এমন কোনও ব্যক্তির কাছে আপনার শংসাপত্রগুলি প্রকাশ করবে। সুতরাং বেশিরভাগ প্রকল্পের জন্য আপনি আপনার নোড ব্যাক এন্ড বা গুগল ক্লাউড ফাংশন থেকে আপনার ফাইলগুলি আপলোড করতে চান, তারপরে ডাউনলোড ইউআরএল পাবেন এবং আপনার ফাইল সম্পর্কিত অন্যান্য ডেটা সহ এটি আপনার ডেটাবেজে সংরক্ষণ করুন save

অস্থায়ী ডাউনলোড URL গুলির জন্য getSignUrl () পান

একটি নোড ব্যাক এন্ড বা গুগল ক্লাউড ফাংশন থেকে getSidedUrl () ব্যবহার করা সহজ:

  function oedPromise() {
    return new Promise(function(resolve, reject) {
      http.get(oedAudioURL, function(response) {
        response.pipe(file.createWriteStream(options))
        .on('error', function(error) {
          console.error(error);
          reject(error);
        })
        .on('finish', function() {
          file.getSignedUrl(config, function(err, url) {
            if (err) {
              console.error(err);
              return;
            } else {
              resolve(url);
            }
          });
        });
      });
    });
  }

একটি স্বাক্ষরিত ডাউনলোড ইউআরএল এর মত দেখাচ্ছে:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio%2FSpanish%2FLatin_America-Sofia-Female-IBM%2Faqu%C3%AD.mp3?GoogleAccessId=languagetwo-cd94d%40appspot.gserviceaccount.com&Expires=4711305600&Signature=WUmABCZIlUp6eg7dKaBFycuO%2Baz5vOGTl29Je%2BNpselq8JSl7%2BIGG1LnCl0AlrHpxVZLxhk0iiqIejj4Qa6pSMx%2FhuBfZLT2Z%2FQhIzEAoyiZFn8xy%2FrhtymjDcpbDKGZYjmWNONFezMgYekNYHi05EPMoHtiUDsP47xHm3XwW9BcbuW6DaWh2UKrCxERy6cJTJ01H9NK1wCUZSMT0%2BUeNpwTvbRwc4aIqSD3UbXSMQlFMxxWbPvf%2B8Q0nEcaAB1qMKwNhw1ofAxSSaJvUdXeLFNVxsjm2V9HX4Y7OIuWwAxtGedLhgSleOP4ErByvGQCZsoO4nljjF97veil62ilaQ%3D%3D

স্বাক্ষরিত ইউআরএলটির মেয়াদ শেষ হওয়ার তারিখ এবং দীর্ঘ স্বাক্ষর রয়েছে। কমান্ড লাইন gsutil signurl -d এর ডকুমেন্টেশন বলে যে স্বাক্ষরিত ইউআরএলগুলি অস্থায়ী: ডিফল্ট মেয়াদোত্তীর্ণতা এক ঘন্টা এবং সর্বাধিক মেয়াদ শেষ হতে সাত দিন।

আমি এখানে রেন্ট করতে চলেছি যে getSidedUrl কখনই বলে না যে আপনার স্বাক্ষরিত ইউআরএল এক সপ্তাহের মধ্যে শেষ হবে। ডকুমেন্টেশন 3-17-2025কোডটির মেয়াদোত্তীকরণের তারিখ রয়েছে, এটি প্রস্তাব করে যে আপনি ভবিষ্যতে মেয়াদোত্তীর্ণ বছর নির্ধারণ করতে পারেন। আমার অ্যাপটি নিখুঁতভাবে কাজ করেছে এবং তারপরে এক সপ্তাহ পরে ক্র্যাশ হয়ে গেছে। ত্রুটি বার্তায় বলা হয়েছে যে স্বাক্ষরগুলি মেলে না, এটি ডাউনলোড URL টি শেষ হয়ে গেছে। আমি আমার কোডে বিভিন্ন পরিবর্তন করেছি, এবং সমস্ত কিছু কাজ করেছে ... যতক্ষণ না এটি এক সপ্তাহ পরে ক্র্যাশ হয়ে যায়। এটি হতাশার এক মাসেরও বেশি সময় ধরে চলেছিল।

আপনার ফাইল সর্বজনীনভাবে উপলব্ধ করুন

ডকুমেন্টেশনে বর্ণিত হিসাবে আপনি নিজের ফাইলের অনুমতিগুলি সর্বজনীন পঠনের জন্য সেট করতে পারেন । এটি ক্লাউড স্টোরেজ ব্রাউজার বা আপনার নোড সার্ভার থেকে করা যেতে পারে। আপনি একটি ফাইল পাবলিক বা ডিরেক্টরি বা আপনার পুরো স্টোরেজ ডাটাবেস করতে পারেন। নোড কোডটি এখানে:

var webmPromise = new Promise(function(resolve, reject) {
      var options = {
        destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.mp3'),
        predefinedAcl: 'publicRead',
        contentType: 'audio/' + audioType,
      };

      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        console.log("webm audio file written.");
        resolve();
      })
      .catch(error => console.error(error));
    });

ফলাফলটি আপনার ক্লাউড স্টোরেজ ব্রাউজারে এটির মতো দেখাবে:

এখানে চিত্র বর্ণনা লিখুন

এরপরে যে কেউ আপনার ফাইলটি ডাউনলোড করতে স্ট্যান্ডার্ড পাথটি ব্যবহার করতে পারেন:

https://storage.googleapis.com/languagetwo-cd94d.appspot.com/Audio/English/United_States-OED-0/system.mp3

একটি ফাইল পাবলিক করার আরেকটি উপায় হ'ল মেইকপাবলিক () পদ্ধতিটি ব্যবহার করা । আমি এটি কাজ করতে সক্ষম হইনি, বালতি এবং ফাইলের পাথগুলি সঠিকভাবে পাওয়া খুব কঠিন।

আকর্ষণীয় বিকল্প হ'ল অ্যাক্সেস কন্ট্রোল তালিকা ব্যবহার করা । আপনি কেবলমাত্র যাদের তালিকায় রেখেছেন কেবল সেই ব্যবহারকারীদের authenticatedReadজন্যই ফাইল সরবরাহ করতে পারেন বা গুগল অ্যাকাউন্ট থেকে লগ ইন হওয়া যে কারও কাছে ফাইলটি উপলব্ধ করতে ব্যবহার করতে পারেন। যদি কোনও বিকল্প থাকে "ফায়ারবেস অথ ব্যবহার করে যে কেউ আমার অ্যাপ্লিকেশনটিতে লগইন করেছে" আমি এটি ব্যবহার করব, কারণ এটি কেবলমাত্র আমার ব্যবহারকারীদের অ্যাক্সেসকে সীমাবদ্ধ করবে।

ফায়ারবেস স্টোরেজডাউনলোডটোকেন সহ আপনার নিজের ডাউনলোড URL টি তৈরি করুন

বেশ কয়েকটি উত্তর একটি অননুমোদিত গুগল স্টোরেজ অবজেক্টের সম্পত্তি বর্ণনা করে firebaseStorageDownloadTokens। এটির সাহায্যে আপনি স্টোরেজটি যে টোকেনটি ব্যবহার করতে চান তা বলতে পারবেন। আপনি uuidনোড মডিউলটির সাথে একটি টোকেন তৈরি করতে পারেন । কোডের চার লাইন এবং আপনি আপনার নিজের ডাউনলোডের URL একই ডাউনলোড আপনি যে URL টি কনসোল বা থেকে পেতে নির্মাণ করতে পারেন getDownloadURL()। কোডের চারটি লাইন হ'ল:

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
metadata: { firebaseStorageDownloadTokens: uuid }
https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm') + "?alt=media&token=" + uuid);

প্রসঙ্গে কোডটি এখানে:

var webmPromise = new Promise(function(resolve, reject) {
  var options = {
    destination: ('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.mp3'),
    contentType: 'audio/' + audioType,
    metadata: {
      metadata: {
        firebaseStorageDownloadTokens: uuid,
      }
    }
  };

      synthesizeParams.accept = 'audio/webm';
      var file = bucket.file('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm');
      textToSpeech.synthesize(synthesizeParams)
      .then(function(audio) {
        audio.pipe(file.createWriteStream(options));
      })
      .then(function() {
        resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent('Audio/' + longLanguage + '/' + pronunciation + '/' + word + '.webm') + "?alt=media&token=" + uuid);
      })
      .catch(error => console.error(error));
});

এটি কোনও টাইপো নয় - আপনাকে firebaseStorageDownloadTokensদ্বিগুণ স্তরে বাসা বাঁধতে হবে metadata:!

ডগ স্টিভেনসন উল্লেখ করেছেন যে firebaseStorageDownloadTokensএটি কোনও গুগল ক্লাউড স্টোরেজ বৈশিষ্ট্য নয়। আপনি এটি কোনও গুগল ডকুমেন্টেশনে পাবেন না এবং এটির ভবিষ্যতের সংস্করণে কোনও প্রতিশ্রুতি থাকবে না @google-cloud। আমি পছন্দ করি firebaseStorageDownloadTokensকারণ আমি যা চাই তা পাওয়ার একমাত্র উপায়, তবে এটির একটি "গন্ধ" রয়েছে যা এটি ব্যবহার করা নিরাপদ নয়।

কেন নোড থেকে getDownloadURL () নেই?

ক্লিটন যেমন লিখেছেন, গুগলের উচিত file.getDownloadURL()একটি পদ্ধতি তৈরি করা উচিত @google-cloud/storage(অর্থাত আপনার নোডের ব্যাক এন্ড)। আমি গুগল ক্লাউড ফাংশন থেকে একটি ফাইল আপলোড করতে এবং টোকেন ডাউনলোড URL পেতে চাই।


10
আমি @google-cloud/storageএটির জন্য একটি ইস্যু তৈরি করেছি , নিঃসন্দেহে
থিও চ্যাম্পিয়ন

1
সর্বশেষ মেকপাবলিক () লিঙ্ক।
গালকি

1
দেখে মনে firebaseStorageDownloadTokensহচ্ছে আর কাজ করে না।
ম্যাসন

1
গৃহীত উত্তরটি পরামর্শ দেয় যে অবিরাম ডাউনলোড url পাওয়া সম্ভব নয় যা মেয়াদ শেষ হয় না যা সঠিক নয়। আপনার উত্তরের এখানে বিশদটি দুর্দান্ত এবং সঠিক উত্তর হিসাবে চিহ্নিত করা উচিত। ধন্যবাদ.
দেবমাইক

2
@ থমাস দুর্দান্ত সংক্ষিপ্তসার জন্য ধন্যবাদ! আপনি উল্লেখ করেছেন যে অবিচ্ছিন্ন টোকেন ডাউনলোড ইউআরএল পাওয়ার 3 উপায় রয়েছে তবে আপনি কেবল 2: (ক) ফায়ারবেস স্টোরেজ কনসোল থেকে এবং (খ) ফ্রন্ট এন্ড থেকে getDownload URL () ভাগ করেছেন। আমি ভাবছি তৃতীয় উপায় কি?
সিজেফিলিপ

23

ফাংশন অবজেক্টের প্রতিক্রিয়াতে সাম্প্রতিক পরিবর্তনগুলির সাথে আপনি ডাউনলোড স্ট্রাইলে একসাথে "সেলাই" করার প্রয়োজনীয় সবকিছু পেতে পারেন:

 const img_url = 'https://firebasestorage.googleapis.com/v0/b/[YOUR BUCKET]/o/'
+ encodeURIComponent(object.name)
+ '?alt=media&token='
+ object.metadata.firebaseStorageDownloadTokens;

console.log('URL',img_url);

2
আপনি কি থেকে বস্তুর প্রতিক্রিয়া উল্লেখ করছেন bucket.file().upload()? আমি প্রতিক্রিয়া ডেটাতে কোনও মেটাডেটা সম্পত্তি পাই না এবং এগুলি কীভাবে পাবেন তা সম্পর্কে আমি নিশ্চিত নই firebaseStorageDownloadTokens
ডাইগারটি

এছাড়াও [আপনার বুকেট] হ'ল bucket.name, আপনাকে এটিকে হার্ডকোড বা অতিরিক্ত স্থানীয়
ভেরি

4
এই সমাধানের সাথে সমস্যাটি হ'ল পরিষেবা URLটি হার্ডকোডযুক্ত। ফায়ারবেস / গুগল যদি এটি পরিবর্তন করে তবে এটি ভেঙে যেতে পারে। metadata.mediaLinkসম্পত্তি ব্যবহার করা যেমন একটি সমস্যা প্রতিরোধ করে।
লরেন্ট

2
এটির মতো URL তৈরির ক্ষেত্রে এটি সমর্থিত নয়। এটি আজ কাজ করতে পারে তবে ভবিষ্যতে ভাঙ্গতে পারে। সঠিক ডাউনলোড URL তৈরি করতে আপনার কেবল সরবরাহিত API গুলি ব্যবহার করা উচিত।
ডগ স্টিভেনসন

1
যে হার্ডকডযুক্ত ইউআরএল পরিবর্তন হতে পারে তার উপর নির্ভর করা বাজে পছন্দ।
লরেন্ট

23

আপনি যদি ফায়ারবেস প্রকল্পে কাজ করছেন, তবে আপনি অন্য লাইব্রেরিগুলিকে অন্তর্ভুক্ত না করে বা কোনও শংসাপত্র ফাইল ডাউনলোড না করে আপনি একটি ক্লাউড ফাংশনে স্বাক্ষরিত URL গুলি তৈরি করতে পারেন। আপনাকে কেবল আইএএম এপিআই সক্ষম করতে হবে এবং আপনার বিদ্যমান পরিষেবা অ্যাকাউন্টে একটি ভূমিকা যুক্ত করতে হবে (নীচে দেখুন)।

অ্যাডমিন লাইব্রেরি আরম্ভ করুন এবং আপনার ফাইল হিসাবে সাধারণত একটি ফাইল রেফারেন্স পাবেন:

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

admin.initializeApp(functions.config().firebase)

const myFile = admin.storage().bucket().file('path/to/my/file')

তারপরে আপনি এর সাথে একটি স্বাক্ষরিত URL তৈরি করুন

myFile.getSignedUrl({action: 'read', expires: someDateObj}).then(urls => {
    const signedUrl = urls[0]
})

আপনার ফায়ারবেস পরিষেবা অ্যাকাউন্ট এটি চালানোর জন্য পর্যাপ্ত অনুমতি আছে তা নিশ্চিত করুন

  1. গুগল এপিআই কনসোল এ যান এবং আইএএম এপিআই সক্ষম করুন ( https://console.developers.google.com/apis/api/iam.googleapis.com/overview )
  2. এখনও এপিআই কনসোলে, প্রধান মেনুতে যান, "আইএএম এবং প্রশাসক" -> "আইএএম"
  3. "অ্যাপ ইঞ্জিন ডিফল্ট পরিষেবা অ্যাকাউন্ট" ভূমিকার জন্য সম্পাদনা ক্লিক করুন
  4. "অন্য ভূমিকা যুক্ত করুন" ক্লিক করুন, এবং "পরিষেবা অ্যাকাউন্ট টোকেন ক্রিয়েটার" নামে একটি যুক্ত করুন
  5. পরিবর্তনগুলি প্রচারের জন্য এক মিনিট সংরক্ষণ করুন এবং অপেক্ষা করুন

ভ্যানিলা ফায়ারবেস কনফিগারেশনের মাধ্যমে, প্রথমবার আপনি উপরের কোডটি চালানোর সময় আপনি একটি ত্রুটি সনাক্তকরণ এবং অ্যাক্সেস ম্যানেজমেন্ট (আইএএম) এআইপি এক্সএক্সএক্সএক্সএক্সএক্স-এর আগে ব্যবহার করা হয়নি বা এটি অক্ষম করা হয়েছে। । আপনি যদি ত্রুটি বার্তায় লিঙ্কটি অনুসরণ করেন এবং আইএএম এপিআই সক্ষম করে থাকেন তবে আপনি অন্য একটি ত্রুটি পাবেন: অনুমতি iam.serviceAccounts.signBlob- কে আমার অ্যাকাউন্টে-পরিষেবা অ্যাকাউন্টে এই ক্রিয়াকলাপটি সম্পাদন করা প্রয়োজন । টোকন স্রষ্টার ভূমিকা যুক্ত করা এই দ্বিতীয় অনুমতি ইস্যুটিকে ঠিক করে দেয়।


আমি মূলত এই একই বিবরণ দিয়ে একটি উত্তর রেখে যাচ্ছিলাম যে আমি খুব সহজেই কঠিন উপায়টি আবিষ্কার করেছি - অবশ্যই আশা করি আমি সমাধানগুলি এর আগে খুব আগেই পড়েছি: / এটি আমার পক্ষে 12/12/18 পর্যন্ত কাজ করেছিল। বিস্তারিত নির্দেশাবলীর জন্য ধন্যবাদ, আমাদের নতুনদের জন্য খুব সহায়ক!
কেট

2
আমার স্বাক্ষরিত মেয়াদ 2 সপ্তাহের মধ্যে শেষ হচ্ছে তবে আমি কী ছাড়াই অ্যাডমিন.ইনটিইলাইজ অ্যাপ () ব্যবহার করছি, এই সমস্যা কি? আমার কাছে অ্যাপ ইঞ্জিন অ্যাপ্লিকেশন ডিফল্ট পরিষেবা অ্যাকাউন্টটি "মালিক" এবং ক্লাউড ফাংশনস পরিষেবা এজেন্ট হিসাবে সেট করা আছে, আমি এখনই "মালিক" সরিয়েছি এবং "পরিষেবা অ্যাকাউন্ট টোকেন ক্রিয়েটার" যুক্ত করেছি
অমিত ব্রাভো

2
স্বাক্ষরিত ইউআরএলগুলি 7 দিনের মধ্যে শেষ হয়। আপনি একটি সংক্ষিপ্ত মেয়াদোত্তীকরণের তারিখ নির্ধারণ করতে পারেন তবে আর বেশি নয়।
থমাস ডেভিড কেহো

ইউআরএলটির মেয়াদ শেষ হলে তা রিফ্রেশ করবেন কীভাবে?
মনোজ এমএম

কীভাবে ইউআরএলটিকে দীর্ঘ সময় সেট করতে রিফ্রেশ করবেন?
সাইফাল্লাক

17

সাফল্যের সাথে আমি যে পদ্ধতিটি ব্যবহার করছি তা হ'ল firebaseStorageDownloadTokensফাইলের মেটাডেটাতে নাম প্রয়োগ করা একটি কীতে একটি ইউআইডি ভি 4 মান সেট করা আপলোড শেষ করার পরে ফায়ারবেস এই ইউআরএলগুলি তৈরি করতে কাঠামোর অনুসরণ করে নিজেই ডাউনলোডের ইউআরএল একত্রিত করুন, যেমন:

https://firebasestorage.googleapis.com/v0/b/[BUCKET_NAME]/o/[FILE_PATH]?alt=media&token=[THE_TOKEN_YOU_CREATED]

আমি জানি না এই পদ্ধতিটি ব্যবহার করা কতটা "নিরাপদ" (ফায়ারবেস ভবিষ্যতে ডাউনলোড URL গুলি কীভাবে জেনারেট করে তা পরিবর্তন করতে পারে) তবে এটি কার্যকর করা সহজ।


1
আপনি যেখানে uuid মান সেট করেছেন সেখানে আপনার কি উদাহরণ রয়েছে?
ড্রউ

1
আমি ড্রয়ের মতো একই প্রশ্ন করেছি, আপনি কোথায় মেটাডেটা সেট করবেন? বালতি.আপলোড ফাংশন করার সময় আমি সেট করার চেষ্টা করেছি, কাজ হয়নি।
বৈশাখ শ্রীনীবাসন

1
বৈশাখ, আমি একটি সম্পূর্ণ উত্তর ডাব্লু / উদাহরণ পোস্ট করেছি। আশা করি আপনাকে সাহায্য করবে।
ড্র

আপনি কোথায় / কিভাবে টোকেন তৈরি করবেন?
কোডিবাগস্টাইন

3
আমি এই কৌশলটিকে "নিরাপদ" হিসাবে বিবেচনা করব না, কারণ ডাউনলোডের URLগুলি অস্বচ্ছ হওয়া বোঝায়, যার উপাদানগুলি ভেঙে ফেলা বা একত্রিত করা উচিত নয়।
ডগ স্টিভেনসন

16

যারা ভাবছেন তাদের জন্য ফায়ারবেস অ্যাডমিন এসডিকে পরিষেবা অ্যাকাউন্টাউন্টকি.জসন ফাইলটি কোথায় যাওয়া উচিত। এটি কেবল ফাংশন ফোল্ডারে রাখুন এবং যথারীতি মোতায়েন করুন।

এটি এখনও আমাকে অবাক করে দেয় যে আমরা জাভাস্ক্রিপ্ট এসডিকে যেমন করে মেটাডেটা থেকে ডাউনলোড ইউআরএল পাচ্ছি না কেন। ইউআরএল তৈরি করা যা শেষ পর্যন্ত মেয়াদ শেষ হয়ে যাবে এবং এটি ডেটাবেজে সংরক্ষণ করা বাঞ্ছনীয় নয়।


15

ক্লাউড স্টোরেজ নোডজেএস 1.6.x বা + predefinedAcl: 'publicRead'সহ কোনও ফাইল আপলোড করার সময় আমি বিকল্পটি ব্যবহার করার পরামর্শ দিই :

const options = {
    destination: yourFileDestination,
    predefinedAcl: 'publicRead'
};

bucket.upload(attachment, options);

তারপরে, সর্বজনীন ইউআরএল পাওয়া এত সহজ:

bucket.upload(attachment, options).then(result => {
    const file = result[0];
    return file.getMetadata();
}).then(results => {
    const metadata = results[0];
    console.log('metadata=', metadata.mediaLink);
}).catch(error => {
    console.error(error);
});

2
এটি বাস্তবে কাজ করতে দেখা যায়। আমি এখনও অবধি কেবলমাত্র খারাপ দিকটি দেখছি তা হ'ল আপনি যদি কোনও ব্রাউজারের ইউআরএল বারে চিত্রটি হিট করেন তবে এটি চিত্রটি ইনলাইন দেখার পরিবর্তে ডাউনলোড করবে।
মাইকেল জিওভানি পুমো

file.getMetadata () ফাইল রেফারেন্সে সেভ () পদ্ধতিটি ব্যবহার করার পরে আমার জন্য কৌশলটি করেছে। এটি নোডজেএস-এ ফায়ারবেস-অ্যাডমিন এসডিকে ব্যবহার করে।
পাস্কাল লেমার

Didnt কাজ, আমি হচ্ছি বেনামী আহ্বানকারী your_app / image.jpg করার storage.objects.get অ্যাক্সেস নেই
মনোজ এমএম

9

দুঃখিত তবে খ্যাতি হারিয়ে যাওয়ার কারণে আমি আপনার প্রশ্নের উপরে কোনও মন্তব্য পোস্ট করতে পারি না, তাই আমি এটিকে এই উত্তরে অন্তর্ভুক্ত করব।

একটি স্বাক্ষরিত url উত্পন্ন করে উপরে বর্ণিত হিসাবে করুন, তবে সার্ভিস-অ্যাকাউন্ট.জসন ব্যবহার করার পরিবর্তে আমি মনে করি যে আপনি যে পরিষেবা তৈরি করতে পারেন সেই অ্যাকাউন্টঅ্যাকাউন্টকি.জসনটি ব্যবহার করতে হবে (সেই অনুযায়ী আপনার স্থান পরিবর্তন করুন)

https://console.firebase.google.com/project/YOURPROJECTID/settings/serviceaccounts/adminsdk

উদাহরণ:

const gcs = require('@google-cloud/storage')({keyFilename: 'serviceAccountKey.json'});
// ...
const bucket = gcs.bucket(bucket);
// ...
return bucket.upload(tempLocalFile, {
        destination: filePath,
        metadata: {
          contentType: 'image/jpeg'
        }
      })
      .then((data) => {
        let file = data[0]
        file.getSignedUrl({
          action: 'read',
          expires: '03-17-2025'
        }, function(err, url) {
          if (err) {
            console.error(err);
            return;
          }

          // handle url 
        })

9

জেমস ড্যানিয়েলস যে উত্তর দিয়েছেন তাতে আমি মন্তব্য করতে পারি না, তবে আমার মনে হয় এটি পড়া খুব জরুরি।

একটি স্বাক্ষরিত URL- এর মত তিনি কি খুঁজে দেবার অনেক ক্ষেত্রে প্রশংসনীয় জন্য মনে হয় খারাপ এবং সম্ভব বিপজ্জনক । ফায়ারবেসের ডকুমেন্টেশন অনুসারে স্বাক্ষরযুক্ত ইউআরএল কিছু সময়ের পরে শেষ হয়ে যায়, সুতরাং আপনার ডেটাবেসে যুক্ত করা একটি নির্দিষ্ট সময়সীমার পরে খালি ইউআরএল নিয়ে যাবে

এটি সেখানে ডকুমেন্টেশনকে ভুল বুঝে থাকতে পারে এবং স্বাক্ষরিত ইউআরএলটির মেয়াদ শেষ হয় না, যার ফলস্বরূপ কিছু সুরক্ষা সমস্যা দেখা দিতে পারে। প্রতিটি আপলোড করা ফাইলের জন্য কীটি একই বলে মনে হচ্ছে। এর অর্থ একবার আপনি একবার কোনও ফাইলের ইউআরএল পেয়ে গেলে, কেউ সহজেই কেবল তাদের নামগুলি জানার মাধ্যমে ফাইলগুলি অ্যাক্সেস করতে পারে যা সে অ্যাক্সেস করার প্রস্তাব দেয় না।

যদি আমি ভুল বুঝতে পারি তবে আমি সংশোধন করতে ভালবাসি। অন্য কেউ সম্ভবত উপরে বর্ণিত সমাধান আপডেট করা উচিত। আমি যদি ভুল হতে পারে


7

আমি বর্তমানে এটি ব্যবহার করি এটি সহজ এবং এটি নির্বিঘ্নে কাজ করে।

গুগল ক্লাউড দিয়ে আপনার কিছু করার দরকার নেই। এটি ফায়ারবেস দিয়ে বাক্সের বাইরে কাজ করে ..

// Save the base64 to storage.
const file = admin.storage().bucket('url found on the storage part of firebase').file(`profile_photos/${uid}`);
await file.save(base64Image, {
    metadata: {
      contentType: 'image/jpeg',
    },
    predefinedAcl: 'publicRead'
});
const metaData = await file.getMetadata()
const url = metaData[0].mediaLink

সম্পাদনা: একই উদাহরণ, তবে আপলোড সহ:

await bucket.upload(fromFilePath, {destination: toFilePath});
file = bucket.file(toFilePath);
metaData = await file.getMetadata()
const trimUrl = metaData[0].mediaLink

হালনাগাদ:

মেটাডেটা পেতে আপলোড পদ্ধতিতে দুটি পৃথক কল করার দরকার নেই:

let file = await bucket.upload(fromFilePath, {destination: toFilePath});
const trimUrl = file[0].metaData.mediaLink

1
বেসড 64 এনকোডড নয় এমন কোনও ফাইলের সাহায্যে আপনি কীভাবে এটি ব্যবহার করবেন?
টিবোর উদवारी

1
এটি মিডিয়ালিঙ্ক্টর নয়, এর স্রেফ মিডিয়া লিংক
l2aelba


@ সারাহ আমি টাইপস্ক্রিপ্ট ব্যবহার করে এটি লিখেছিলাম, নিশ্চিত না কোনও মডিউল প্রতিস্থাপন আছে কিনা not
অলিভার ডিকসন

3

আমার একই সমস্যা ছিল, তবে আমি README এর পরিবর্তে ফায়ারবেস ফাংশন উদাহরণের কোডটি দেখছিলাম। এবং এই থ্রেডের উত্তরগুলি কোনওভাবেই সহায়তা করে নি ...

আপনি নিম্নলিখিতটি করে কনফিগারেশন ফাইলটি পাস করা এড়াতে পারবেন:

আপনার প্রকল্পের ক্লাউড কনসোল> আইএএম এবং অ্যাডমিন> আইএএম এ যান , অ্যাপ ইঞ্জিন ডিফল্ট পরিষেবা অ্যাকাউন্টটি সন্ধান করুন এবং সেই সদস্যের সাথে পরিষেবা অ্যাকাউন্ট টোকেন নির্মাতার ভূমিকা যুক্ত করুন। এটি আপনার অ্যাপ্লিকেশনটিকে চিত্রগুলিতে স্বাক্ষরিত সর্বজনীন URL তৈরি করার অনুমতি দেবে।

উত্স: থাম্বনেইলগুলি ফাংশন README স্বয়ংক্রিয়ভাবে উত্পন্ন করে

অ্যাপ ইঞ্জিনের জন্য আপনার ভূমিকাটি দেখতে এমন হওয়া উচিত:

ক্লাউড কনসোল


3

আপনি যদি 'পাবলিক রিড'-এর পূর্বনির্ধারিত অ্যাক্সেস নিয়ন্ত্রণ তালিকাগুলির মান ব্যবহার করেন তবে আপনি ফাইলটি আপলোড করতে এবং খুব সাধারণ ইউআরএল কাঠামো দিয়ে অ্যাক্সেস করতে পারেন:

// Upload to GCS
const opts: UploadOptions = {
  gzip: true,
  destination: dest, // 'someFolder/image.jpg'
  predefinedAcl: 'publicRead',
  public: true
};
return bucket.upload(imagePath, opts);

এরপরে আপনি এর মতো ইউআরএল তৈরি করতে পারেন:

const storageRoot = 'https://storage.googleapis.com/';
const bucketName = 'myapp.appspot.com/'; // CHANGE TO YOUR BUCKET NAME
const downloadUrl = storageRoot + bucketName + encodeURIComponent(dest);

2

আপনার যদি কেবল সাধারণ URL সহ একটি সর্বজনীন ফাইলের প্রয়োজন হয় তবে এটি কাজ করে। নোট করুন যে এটি আপনার ফায়ারবেস স্টোরেজ নিয়মগুলিকে বাতিল করতে পারে।

bucket.upload(file, function(err, file) {
    if (!err) {
      //Make the file public
      file.acl.add({
      entity: 'allUsers',
      role: gcs.acl.READER_ROLE
      }, function(err, aclObject) {
          if (!err) {
              var URL = "https://storage.googleapis.com/[your bucket name]/" + file.id;
              console.log(URL);
          } else {
              console.log("Failed to set permissions: " + err);
          }
      });  
    } else {
        console.log("Upload failed: " + err);
    }
});

1

যারা ফায়ারবেস এসডিকে ব্যবহার করছেন এবং তাদের জন্য admin.initializeApp:

1 - একটি প্রাইভেট কী তৈরি করুন এবং / ফাংশন ফোল্ডারে রাখুন।

2 - আপনার কোডটি নীচে কনফিগার করুন:

const serviceAccount = require('../../serviceAccountKey.json');
try { admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) })); } catch (e) {}

নথিপত্র

চেষ্টা / ধরার কারণ আমি এমন একটি index.js ব্যবহার করছি যা অন্যান্য ফাইল আমদানি করে এবং প্রতিটি ফাইলের জন্য একটি ফাংশন তৈরি করে। আপনি যদি সমস্ত ফাংশন সহ একটি একক index.js ফাইল ব্যবহার করেন তবে আপনার সাথে ঠিক থাকা উচিত admin.initializeApp(Object.assign(functions.config().firebase, { credential: admin.credential.cert(serviceAccount) }));


আমার জন্য এটি ছিল '../serviceaccountkey.json' তবে মাথা ব্যবহারের জন্য ধন্যবাদ ../
রবার্ট কিং

1

ফায়ারবেস .0.০.০ হিসাবে আমি সরাসরি অ্যাডমিনের সাথে স্টোরেজ অ্যাক্সেস করতে সক্ষম হয়েছি:

const bucket = admin.storage().bucket();

সুতরাং আমার কোনও পরিষেবা অ্যাকাউন্ট যুক্ত করার দরকার নেই। তারপরে উপরের রেফারেন্স হিসাবে ইউআইডিটি সেট করে ফায়ারবেস ইউআরএল পাওয়ার জন্য কাজ করেছে।


1

এটিই আমি সেরা এসেছি। এটি অনর্থক, তবে একমাত্র যুক্তিযুক্ত সমাধান যা আমার পক্ষে কাজ করেছিল।

await bucket.upload(localFilePath, {destination: uploadPath, public: true});
const f = await bucket.file(uploadPath)
const meta = await f.getMetadata()
console.log(meta[0].mediaLink)

1

signedURL()ব্যবহার না করেmakePublic()

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp()
var bucket = admin.storage().bucket();

// --- [Above] for admin related operations, [Below] for making a public url from a GCS uploaded object

const { Storage } = require('@google-cloud/storage');
const storage = new Storage();

exports.testDlUrl = functions.storage.object().onFinalize(async (objMetadata) => {
    console.log('bucket, file', objMetadata.bucket + ' ' + objMetadata.name.split('/').pop()); // assuming file is in folder
    return storage.bucket(objMetadata.bucket).file(objMetadata.name).makePublic().then(function (data) {
        return admin.firestore().collection('publicUrl').doc().set({ publicUrl: 'https://storage.googleapis.com/' + objMetadata.bucket + '/' + objMetadata.name }).then(writeResult => {
            return console.log('publicUrl', writeResult);
        });
    });
});


0

আপনি যদি ত্রুটি পেয়ে থাকেন:

গুগল ক্লাউড ফাংশন: প্রয়োজনীয় (…) কোনও ফাংশন নয়

এটা চেষ্টা কর:

const {Storage} = require('@google-cloud/storage');
const storage = new Storage({keyFilename: 'service-account-key.json'});
const bucket = storage.bucket(object.bucket);
const file = bucket.file(filePath);
.....

0

আমি ইতিমধ্যে আমার উত্তরগুলি পোস্ট করেছি ... নীচে ইউআরএল এ যেখানে আপনি সমাধান সহ পুরো কোড পেতে পারেন

নোড.জেএস ব্যবহার করে আমি কীভাবে কোনও বেস 64 এনকোডযুক্ত চিত্র (স্ট্রিং) সরাসরি গুগল ক্লাউড স্টোরেজ বালতিতে আপলোড করব?

const uuidv4 = require('uuid/v4');
const uuid = uuidv4();

    const os = require('os')
    const path = require('path')
    const cors = require('cors')({ origin: true })
    const Busboy = require('busboy')
    const fs = require('fs')
    var admin = require("firebase-admin");


    var serviceAccount = {
        "type": "service_account",
        "project_id": "xxxxxx",
        "private_key_id": "xxxxxx",
        "private_key": "-----BEGIN PRIVATE KEY-----\jr5x+4AvctKLonBafg\nElTg3Cj7pAEbUfIO9I44zZ8=\n-----END PRIVATE KEY-----\n",
        "client_email": "xxxx@xxxx.iam.gserviceaccount.com",
        "client_id": "xxxxxxxx",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-5rmdm%40xxxxx.iam.gserviceaccount.com"
      }

    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount),
        storageBucket: "xxxxx-xxxx" // use your storage bucket name
    });


    const app = express();
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
app.post('/uploadFile', (req, response) => {
    response.set('Access-Control-Allow-Origin', '*');
    const busboy = new Busboy({ headers: req.headers })
    let uploadData = null
    busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
        const filepath = path.join(os.tmpdir(), filename)
        uploadData = { file: filepath, type: mimetype }
        console.log("-------------->>",filepath)
        file.pipe(fs.createWriteStream(filepath))
      })

      busboy.on('finish', () => {
        const bucket = admin.storage().bucket();
        bucket.upload(uploadData.file, {
            uploadType: 'media',
            metadata: {
              metadata: { firebaseStorageDownloadTokens: uuid,
                contentType: uploadData.type,
              },
            },
          })

          .catch(err => {
            res.status(500).json({
              error: err,
            })
          })
      })
      busboy.end(req.rawBody)
   });




exports.widgets = functions.https.onRequest(app);
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.