web-dev-qa-db-fra.com

Puis-je définir le nom de fichier d'un PDF objet affiché dans Chrome?

Dans mon application Vue, je reçois un PDF en tant que blob et je souhaite l'afficher à l'aide de la visionneuse PDF du navigateur.

Je le convertis en fichier et génère un objet URL:

const blobFile = new File([blob], `my-file-name.pdf`, { type: 'application/pdf' })
this.invoiceUrl = window.URL.createObjectURL(blobFile)

Ensuite, je l'affiche en définissant cette URL en tant qu'attribut data d'un élément d'objet.

<object
  :data="invoiceUrl"
  type="application/pdf"
  width="100%"
  style="height: 100vh;">
</object>

Le navigateur affiche ensuite le PDF à l'aide du visualiseur PDF. Cependant, dans Chrome, le nom du fichier que je fournis (ici, mon-nom-fichier.pdf) n'est pas utilisé: je vois un hachage dans la barre de titre de la visionneuse PDF et lorsque je télécharge le fichier à l'aide de soit un clic droit -> Enregistrer sous ... ou les commandes du visualiseur, il enregistre le fichier avec le hachage du blob (cda675a6-10af-42f3-aa68-8795aa8c377d ou similaire).

La visionneuse et le nom du fichier fonctionnent comme je l'avais espéré dans Firefox; c'est uniquement Chrome dans lequel le nom de fichier n'est pas utilisé.

Existe-t-il un moyen, à l'aide de Javascript natif (y compris ES6, mais pas de dépendances tierces autres que Vue), de définir le nom de fichier d'un élément blob/object dans Chrome?

[edit] Si cela vous aide, la réponse contient les en-têtes suivants:

Content-Type: application/pdf; charset=utf-8
Transfer-Encoding: chunked
Content-Disposition: attachment; filename*=utf-8''Invoice%2016246.pdf;
Content-Description: File Transfer
Content-Encoding: gzip
5
false_azure

L'extension de Chrome semble reposer sur le nom de la ressource défini dans l'URI, c'est-à-dire le file.ext dans protocol://domain/path/file.ext.

Donc, si votre URI d'origine contient ce nom de fichier, le plus simple serait peut-être simplement de transformer la variable data de votre <objet> en URI à partir duquel vous avez directement récupéré le fichier PDF, au lieu de suivre le style de Blob. 

Dans certains cas, cela ne peut pas être fait et, pour ceux-ci, il existe une méthode convoluted, qui pourrait ne pas fonctionner dans les versions futures de Chrome, et probablement pas dans d'autres navigateurs, nécessitant de configurer un Travailleur de service .

Comme nous l'avons dit en premier lieu, Chrome analyse l'URI à la recherche d'un nom de fichier. Ce que nous devons faire, c'est donc avoir un URI, avec ce nom de fichier, qui pointe vers notre BlobURI.

Et pour pouvoir le faire, le seul moyen que j’ai trouvé pour le moment est de 

  1. À partir du document, faites une demande POST, qui enverra le blob à notre ServiceWorker.
  2. Dans ServiceWorker, mettez en cache le blob envoyé
  3. Toujours depuis ServiceWorker, renvoyez un nouvel URI factice que nous mapperons avec le blob mis en cache dans 2.
  4. Dans le document, définissez la src d'un <iframe>1 à ce faux URI.
  5. Dans ServiceWorker, saisissez la demande et envoyez plutôt notre blob en cache.
  6. À partir du document, profitez-en.

1.Je ne pouvais pas voir les requêtes effectuées à partir d'une balise <object> dans Chrome, il est donc probablement préférable d'utiliser un iframe ici (cela aurait de meilleurs résultats dans IE également)).

Ou en code, 

Dans document.html

// register our ServiceWorker
navigator.serviceWorker.register('/sw.js')
   .then(...
...

function displayRenamedPDF(file, filename) {
  // we use an hard-coded fake path
  // that we will check in every requests from the SW
  const reg_path = '/nameForcer_register/'
  // pass the filename
  const url = reg_path + encodeURIComponent(filename);
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url);
  return new Promise((res, rej) => {
    xhr.onload = e => {
        const frame = document.createElement('iframe');
        frame.src = xhr.response;
        document.body.append(frame);
        return frame;
    };
    xhr.send(file);
  })
}

Dans le ServiceWorker sw.js

self.addEventListener('fetch', function(event) {
  const req = event.request,
    url = new URL(req.url),
    // parse the /path/ from every request url
    pathes = url.pathname.split('/'),
    // are we registering
    nameRegIndex = pathes.indexOf('nameForcer_register'),
    // or fetching
    nameFetcherIndex = pathes.indexOf('nameForcer_fetch');

  if(nameRegIndex > -1) { // register
    event.respondWith(
      req.blob() // grab the POSTed Blob
        .then((blob) => {
          const filename = pathes[nameRegIndex+1] || '';
          // store in our db object
          db[filename] = blob;
          return filename;
        })
        .then((filename) =>
          new Response('/nameForcer_fetch/' + filename)
        )
    );
  }
  else if(nameFetcherIndex > -1) { // fetch
    const filename = pathes[nameFetcherIndex + 1];
    const cached = db[filename];
    // just for Firefox, Chrome doesn't care...
    const headers = new Headers({
      'Content-Disposition': 'inline; filename="' + decodeURIComponent(filename) + '"'
    });
    event.respondWith(
      new Response(cached, {
        headers: headers
      })
    );
    delete db[filename];     // !! one time URI !!
  }
  else { // normal requests
    event.respondWith(fetch(event.request))
  }
});

Malheureusement, je n’ai pas pu faire fonctionner ServiceWorker sur plnkr.co, mais vous pouvez toujours trouver l’ensemble configuration ici que vous devriez pouvoir copier sur votre hôte local.


Et une autre solution, je n'ai pas pris le temps de vérifier par moi-même, serait de lancer votre propre visionneur de pdf.

Mozilla a rendu son plugin basé sur js pdf.js disponible, nous devrions donc pouvoir définir le nom du fichier (même si, encore une fois, je n’ai pas encore creusé là-bas).


Enfin, Firefox peut utiliser la propriété name d'un objet File sur lequel un blobURI pointe vers . Ainsi, même si ce n'est pas ce que l'OP a demandé, dans FF, il suffit 

const file = new File([blob], filename);
const url = new URL(blob);
object.data = url;
2
Kaiido

Si vous demandez la ressource via votre propre serveur, vous pouvez utiliser l'en-tête de réponse Content-Disposition pour transmettre le nom du fichier dans la réponse.

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"

Vous pouvez le remplacer par votre nom de fichier et votre extension. Vous pouvez jeter un oeil à ce link pour explorer cet en-tête plus en fonction de vos besoins.

0
Pranay Tripathi

Dans Chrome, le nom de fichier est dérivé de l'URL. Par conséquent, tant que vous utilisez une URL de blob, la réponse courte est "Non, vous ne pouvez pas définir le nom de fichier d'un objet PDF affiché dans Chrome". Vous n'avez aucun contrôle sur l'UUID attribué à l'URL du blob et vous ne pouvez pas le remplacer par le nom de la page à l'aide de l'élément object. Il est possible qu’à l’intérieur du PDF un titre soit spécifié et qu’il apparaisse dans le visualiseur PDF comme nom du document, mais que vous obteniez toujours le nom de hachage lors du téléchargement. 

Cela semble être une mesure de sécurité, mais je ne peux pas le dire avec certitude.

Bien sûr, si vous avez le contrôle sur l'URL, vous pouvez facilement définir le nom de fichier PDF en modifiant l'URL. 

0
Old Pro