I am using a flask backend and flutter frontend to build a website. I am trying to make it have the functionality of uploading a document on one upload.dart page (to an azure storage), and then displaying the document on a confirmation uploaded.dart page. However, I'm just getting a blank screen when I run my code to display the document (other things appear, but the space for the image/pdf is blank).
This is my app.py flask code
from flask import Flask
from flask import send_from_directory, render_template
from flask import redirect, url_for, request, jsonify
import os
from datetime import datetime, timedelta
from werkzeug.utils import secure_filename
from flask_cors import CORS
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient
from azure.storage.blob import generate_blob_sas, BlobSasPermissions
app = Flask(__name__)
azure_blob_storage_origin = "https://aishore.blob.core.windows.net"
CORS(app, resources={r"/*": {"origins": [azure_blob_storage_origin, "*"]}})
connect_str = 'DefaultEndpointsProtocol=CONNECTION_STRING_ETC'
if not connect_str:
raise ValueError("Please set the AZURE_STORAGE_CONNECTION_STRING environment variable")
container_name = "documents" # azure storage account container name
blob_service_client = BlobServiceClient.from_connection_string(conn_str=connect_str)
# generates container if it doesn't already exist
try:
container_client = blob_service_client.get_container_client(container=container_name)
container_client.get_container_properties()
except Exception as e:
container_client = blob_service_client.create_container(container_name) # create a container in the storage account if it does not exist
@app.route('/')
def render_page():
return render_template('/index.html')
# uploading documents page
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({
"message": "no file available",
"status": "fail"
}), 400
file = request.files['file']
if file.filename == '':
return jsonify({
"message": "no selected file",
"status": "fail"
}), 400
if file:
timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
filename = secure_filename(f"{timestamp}_{file.filename}")
try:
blob_client = container_client.get_blob_client(filename)
blob_client.upload_blob(file.stream, overwrite=True)
file_url = blob_client.url
return jsonify({
"message": "File uploaded successfully",
"status": "success",
"file_url": file_url,
}), 200
except Exception as e:
return jsonify({
"message": "Failed to upload file, Error ${e}",
"status": "fail",
}), 500
# rerouting for uploaded files to either display the image or provide a download link to the pdf
@app.route('/upload/<path:filename>', methods=['GET'])
def uploaded_file(filename):
try:
blob_client = container_client.get_blob_client(filename)
sas_token = generate_blob_sas(
account_name=blob_service_client.account_name,
container_name=container_name,
blob_name=blob_client.blob_name,
account_key=blob_service_client.credential.account_key,
permission=BlobSasPermissions(read=True),
expiry=datetime.now(datetime.UTC)() + timedelta(hours=1)
)
signed_url = f"{blob_client.primary_endpoint}/{container_name}/{blob_client.blob_name}?{sas_token}"
return jsonify({
"file_url": signed_url,
"status": "success"
})
except Exception as e:
return jsonify({"message": f"Failed to retrieve file: {str(e)}", "status": "fail"}), 500
if __name__ == '__main__':
app.run(debug=True)
This is my uploaded.dart flutter code:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'dart:html' as html;
import 'package:http/http.dart' as http;
class UploadedPage extends StatefulWidget {
final String signedUrl;
const UploadedPage({
super.key,
required this.signedUrl,
});
@override
State<UploadedPage> createState() => _UploadedPageState();
}
class _UploadedPageState extends State<UploadedPage> {
Widget _displayPDFInIframe(String url) {
return SizedBox(
height: 600,
width: 800,
child: HtmlElementView(
viewType: 'iframe',
key: UniqueKey(),
onPlatformViewCreated: (int viewId) {
final iframe = html.IFrameElement()
..src = url
..style.border = 'none'
..style.width = '100%'
..style.height = '100%';
html.document.getElementById('iframe_$viewId')?.append(iframe);
},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'File uploaded successfully!',
style: TextStyle(fontSize: 24),
),
const SizedBox(height: 16.0),
SelectableText('TEMP URL ${widget.signedUrl}'),
const SizedBox(height: 16.0),
_displayPDFInIframe(widget.signedUrl),
],
),
),
),
);
}
}
Sorry for the code spam, I'm not sure what part of the code I might be having issues.
Because I'm using an SAS token (or trying to) my access level is set to private. However, I did notice that when I printed widget.signedUrl
on the website, it doesn't have an SAS token, which I'm not sure is the cause of the issue.
I've tried changing the code in app.py to make signedUrl have the SAS token but have been getting errors when doing that.
I've also tried not using an iframe, but no matter what I try, I either get error icons or just nothing at all. I can provide whatever other code I've tried but I think using iframes is closest to what I might want.
Check the below Flask
server code for uploading files to Azure Blob Storage and retrieving signed URLs for those files.
On the client side, there's a Flutter
application designed to fetch and display files (images and PDFs) from the Flask server.
The below Flask code uploads files to Azure Blob Storage and generate SAS (Shared Access Signature) URLs for downloading those files.
Added cors CORS(app, resources={r"/upload/*": {"origins": "*"}})
to allow cors from all urls without this I was facing cors issue in Flutter
.
from flask import Flask, render_template, jsonify, request
.....
app = Flask(__name__)
CORS(app, resources={r"/upload/*": {"origins": "*"}}) # Specify the resource path for CORS
.....
....
Make sure to add cors in the Storage account
The Flutter code fetches and displays an image from a URL endpoint using HTTP requests.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_cached_pdfview/flutter_cached_pdfview.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Display image from URL',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DisplayFileFromUrl(),
);
}
}
class DisplayFileFromUrl extends StatefulWidget {
DisplayFileFromUrl({Key? key}) : super(key: key);
@override
_DisplayFileFromUrlState createState() => _DisplayFileFromUrlState();
}
class _DisplayFileFromUrlState extends State<DisplayFileFromUrl> {
String? fileUrl;
bool isLoading = false;
late String baseUrl;
late String filename;
@override
void initState() {
super.initState();
baseUrl = 'http://127.0.0.1:5000/upload/';
filename = 'sample.jpg/.pdf'; // Replace with your filename
fetchFile();
}
Future<void> fetchFile() async {
setState(() {
isLoading = true;
});
final String url = '$baseUrl$filename';
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final Map<String, dynamic> responseData = json.decode(response.body);
fileUrl = responseData['file_url'];
setState(() {
isLoading = false;
});
} else {
throw Exception('Failed to load file');
}
} catch (error) {
print('Error fetching file: $error');
setState(() {
isLoading = false;
});
}
}
Widget buildFileWidget() {
if (isLoading) {
return Center(child: CircularProgressIndicator());
} else if (fileUrl != null) {
if (fileUrl!.toLowerCase().endsWith('.pdf')) {
return PDF().cachedFromUrl(fileUrl!);
} else {
return CachedNetworkImage(
imageUrl: fileUrl!,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
fit: BoxFit.contain,
);
}
} else {
return Center(child: Text('File not found'));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Display File from URL'),
),
body: Center(
child: buildFileWidget(),
),
);
}
}
Image display:
For PDF display, I am getting a black page. I followed this document and used flutter_pdfview, but encountered the error "TargetPlatform.windows is not yet supported by the pdfview_flutter plugin."
This package documentation clearly says that flutter_pdfview is supported only for iOS and Android platforms. Consider the syncfusion_flutter_pdfviewer
package instead.