title: website downloading Amazon Polly's voice as mp3 file
body: <p>I'm a beginner in coding. I would like to make a simple website using Amazon Polly.</p>
  <ol>
  <li>a web site with a text box</li>
  <li>you write a text in the text box and click a button &quot;Read&quot;</li>
  <li>you can download a mp3 file which is made by Amazon Polly</li>
  </ol>
  <p>I'm an English teacher in Japan, so I would like my students to use this website to improve their English pronunciation.</p>
  <p>I have realized 1 and 2, but not 3. I can't find the way to download AudioStream as a mp3 file.
  I use flask, Amazon Polly and AWS Elastic Beanstalk.</p>
  <p>This is application.py</p>
  <pre><code>from argparse import ArgumentParser
  from flask import Flask, jsonify, Response, render_template, request, send_file
  import os
  import sys
  
  from boto3 import Session
  from botocore.exceptions import BotoCoreError, ClientError
  
  # Mapping the output format used in the client to the content type for the
  # response
  AUDIO_FORMATS = {&quot;ogg_vorbis&quot;: &quot;audio/ogg&quot;,
                   &quot;mp3&quot;: &quot;audio/mpeg&quot;,
                   &quot;pcm&quot;: &quot;audio/wave; codecs=1&quot;}
  
  # Create a client using the credentials and region defined in the adminuser
  # section of the AWS credentials and configuration files
  session = Session(aws_access_key_id='???', aws_secret_access_key='???', region_name='us-east-1')
  polly = session.client(&quot;polly&quot;)
  
  # Create a flask app
  application = Flask(__name__)
  
  
  # Simple exception class
  class InvalidUsage(Exception):
      status_code = 400
  
      def __init__(self, message, status_code=None, payload=None):
          Exception.__init__(self)
          self.message = message
          if status_code is not None:
              self.status_code = status_code
          self.payload = payload
  
      def to_dict(self):
          rv = dict(self.payload or ())
          rv['message'] = self.message
          return rv
  
  
  # Register error handler
  @application.errorhandler(InvalidUsage)
  def handle_invalid_usage(error):
      response = jsonify(error.to_dict())
      response.status_code = error.status_code
      return response
  
  
  @application.route('/', methods=['GET'])
  def index():
      return render_template('index.html')
  
  
  @application.route('/read', methods=['GET'])
  def read():
      &quot;&quot;&quot;Handles routing for reading text (speech synthesis)&quot;&quot;&quot;
      # Get the parameters from the query string
      try:
          outputFormat = request.args.get('outputFormat')
          text = request.args.get('text')
          voiceId = request.args.get('voiceId')
      except TypeError:
          raise InvalidUsage(&quot;Wrong parameters&quot;, status_code=400)
  
      # Validate the parameters, set error flag in case of unexpected
      # values
      if len(text) == 0 or len(voiceId) == 0 or \
              outputFormat not in AUDIO_FORMATS:
          raise InvalidUsage(&quot;Wrong parameters&quot;, status_code=400)
      else:
          try:
              # Request speech synthesis
              response = polly.synthesize_speech(Text='&lt;speak&gt;&lt;amazon:domain name=&quot;conversational&quot;&gt;&lt;prosody rate=&quot;slow&quot;&gt;' + text + '&lt;/prosody&gt;&lt;/amazon:domain&gt;&lt;/speak&gt;',
                                                 VoiceId=voiceId, Engine='neural', TextType='ssml',
                                                 OutputFormat=outputFormat)
          except (BotoCoreError, ClientError) as err:
              # The service returned an error
              raise InvalidUsage(str(err), status_code=500)
  
          return send_file(response.get(&quot;AudioStream&quot;),
                           AUDIO_FORMATS[outputFormat])
  
  
  # Define and parse the command line arguments
  cli = ArgumentParser(description='Example Flask Application')
  cli.add_argument(
      &quot;-p&quot;, &quot;--port&quot;, type=int, metavar=&quot;PORT&quot;, dest=&quot;port&quot;, default=8000)
  cli.add_argument(
      &quot;--host&quot;, type=str, metavar=&quot;HOST&quot;, dest=&quot;host&quot;, default=&quot;localhost&quot;)
  arguments = cli.parse_args()
  
  
  # If the module is invoked directly, initialize the application
  if __name__ == '__main__':
      # Configure and run flask app
      application.secret_key = os.urandom(24)
      application.debug = True
      application.run(arguments.host, arguments.port)
  </code></pre>
  <p>This is index.html</p>
  <pre><code>&lt;html&gt;
  
  &lt;head&gt;
      &lt;title&gt;Text-to-Speech Example Application&lt;/title&gt;
      &lt;script&gt;
          /*
           * This sample code requires a web browser with support for both the
           * HTML5 and ECMAScript 5 standards; the following is a non-comprehensive
           * list of compliant browsers and their minimum version:
           *
           * - Chrome 23.0+
           * - Firefox 21.0+
           * - Internet Explorer 9.0+
           * - Edge 12.0+
           * - Opera 15.0+
           * - Safari 6.1+
           * - Android (stock web browser) 4.4+
           * - Chrome for Android 51.0+
           * - Firefox for Android 48.0+
           * - Opera Mobile 37.0+
           * - iOS (Safari Mobile and Chrome) 3.2+
           * - Internet Explorer Mobile 10.0+
           * - Blackberry Browser 10.0+
           */
  
          // Mapping of the OutputFormat parameter of the SynthesizeSpeech API
          // and the audio format strings understood by the browser
          var AUDIO_FORMATS = {
              'ogg_vorbis': 'audio/ogg',
              'mp3': 'audio/mpeg',
              'pcm': 'audio/wave; codecs=1'
          };
  
          /**
           * Handles fetching JSON over HTTP
           */
          function fetchJSON(method, url, onSuccess, onError) {
              var request = new XMLHttpRequest();
              request.open(method, url, true);
              request.onload = function () {
                  // If loading is complete
                  if (request.readyState === 4) {
                      // if the request was successful
                      if (request.status === 200) {
                          var data;
  
                          // Parse the JSON in the response
                          try {
                              data = JSON.parse(request.responseText);
                          } catch (error) {
                              onError(request.status, error.toString());
                          }
  
                          onSuccess(data);
                      } else {
                          onError(request.status, request.responseText)
                      }
                  }
              };
  
              request.send();
          }
  
          /**
           * Returns a list of audio formats supported by the browser
           */
          function getSupportedAudioFormats(player) {
              return Object.keys(AUDIO_FORMATS)
                  .filter(function (format) {
                      var supported = player.canPlayType(AUDIO_FORMATS[format]);
                      return supported === 'probably' || supported === 'maybe';
                  });
          }
  
          // Initialize the application when the DOM is loaded and ready to be
          // manipulated
          document.addEventListener(&quot;DOMContentLoaded&quot;, function () {
              var input = document.getElementById('input'),
                  voiceMenu = document.getElementById('voice'),
                  text = document.getElementById('text'),
                  player = document.getElementById('player'),
                  submit = document.getElementById('submit'),
                  supportedFormats = getSupportedAudioFormats(player);
  
              // Display a message and don't allow submitting the form if the
              // browser doesn't support any of the available audio formats
              if (supportedFormats.length === 0) {
                  submit.disabled = true;
                  alert('The web browser in use does not support any of the' +
                        ' available audio formats. Please try with a different' +
                        ' one.');
              }
  
              // Play the audio stream when the form is submitted successfully
              input.addEventListener('submit', function (event) {
                  // Validate the fields in the form, display a message if
                  // unexpected values are encountered
                  if (text.value.length === 0) {
                      alert('Please fill in all the fields.');
                  } else {
                      var selectedVoice = voiceMenu
                                              .options[voiceMenu.selectedIndex]
                                              .value;
  
                      // Point the player to the streaming server
                      player.src = '/read?voiceId=' +
                          encodeURIComponent(selectedVoice) +
                          '&amp;text=' + encodeURIComponent(text.value) +
                          '&amp;outputFormat=' + supportedFormats[0];
                      player.play();
                  }
  
                  // Stop the form from submitting,
                  // Submitting the form is allowed only if the browser doesn't
                  // support Javascript to ensure functionality in such a case
                  event.preventDefault();
              });
          });
  
      &lt;/script&gt;
      &lt;style&gt;
          #input {
              min-width: 100px;
              max-width: 600px;
              margin: 0 auto;
              padding: 50px;
          }
  
          #input div {
              margin-bottom: 20px;
          }
  
          #text {
              width: 100%;
              height: 200px;
              display: block;
          }
  
          #submit {
              width: 100%;
          }
      &lt;/style&gt;
  &lt;/head&gt;
  
  &lt;body&gt;
      &lt;form id=&quot;input&quot; method=&quot;GET&quot; action=&quot;/read&quot;&gt;
          &lt;div&gt;
              &lt;label for=&quot;voice&quot;&gt;Select a voice:&lt;/label&gt;
              &lt;select id=&quot;voice&quot; name=&quot;voiceId&quot;&gt;
                  &lt;option value=&quot;Joanna&quot;&gt;Joanna&lt;/option&gt;
                  &lt;option value=&quot;Matthew&quot;&gt;Matthew&lt;/option&gt;
              &lt;/select&gt;
          &lt;/div&gt;
          &lt;div&gt;
              &lt;label for=&quot;text&quot;&gt;Text to read:&lt;/label&gt;
              &lt;textarea id=&quot;text&quot; maxlength=&quot;1000&quot; minlength=&quot;1&quot; name=&quot;text&quot;
                      placeholder=&quot;Type some text here...&quot;&gt;&lt;/textarea&gt;
          &lt;/div&gt;
          &lt;input type=&quot;submit&quot; value=&quot;Read&quot; id=&quot;submit&quot; /&gt;
      &lt;/form&gt;
      &lt;audio id=&quot;player&quot;&gt;&lt;/audio&gt;
  &lt;/body&gt;
  
  &lt;/html&gt;
  </code></pre>
  <p>I think I should add a javascript code like this. However, I don't know what to put in &quot;var content&quot; and I don't know where to put this javascript code in index.html.</p>
  <pre><code>&lt;body&gt;
      &lt;script type='text/javascript'&gt;
          function handleDownload() {
              var content = ??????;
              var blob = new Blob([ content ], { &quot;type&quot; : &quot;audio/mp3&quot; });                
              document.getElementById(&quot;download&quot;).href = window.URL.createObjectURL(blob);  
          }
      &lt;/script&gt;
      &lt;a id=&quot;download&quot; href=&quot;#&quot; download=&quot;test.mp3&quot; onclick=&quot;handleDownload()&quot;&gt;downloadMP3&lt;/a&gt;
  &lt;/body&gt;
  </code></pre>
  <p>Could you give me any information or suggestion?</p>
  <p>Thank you in advance.</p>
  <p>Sincerely, Kazu</p>