Для создания веб-сайта я использую серверную часть Flask и интерфейс Flutter. Я пытаюсь реализовать функцию загрузки документа на одну страницу upload.dart (в хранилище Azure), а затем отображать документ на странице подтверждения upload.dart. Однако я просто получаю пустой экран, когда запускаю код для отображения документа (появляются другие вещи, но место для изображения/pdf пусто).
Это мой код фляги app.py
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)
Это мой загруженный флаттер-код .dart:
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),
],
),
),
),
);
}
}
Извините за спам с кодом, я не уверен, с какой частью кода у меня могут возникнуть проблемы.
Поскольку я использую токен SAS (или пытаюсь это сделать), мой уровень доступа установлен на частный. Однако я заметил, что когда я распечатал widget.signedUrl
на веб-сайте, на нем нет токена SAS, и я не уверен, что это является причиной проблемы.
Я попытался изменить код в app.py, чтобы у SignedUrl был токен SAS, но при этом возникли ошибки.
Я также пробовал не использовать iframe, но что бы я ни пытался, я получаю либо значки ошибок, либо вообще ничего. Я могу предоставить любой другой код, который я пробовал, но я думаю, что использование iframes ближе всего к тому, что мне может понадобиться.
Проверьте приведенный ниже код сервера Flask
для загрузки файлов в хранилище BLOB-объектов Azure и получения подписанных URL-адресов для этих файлов.
На стороне клиента есть приложение Flutter
, предназначенное для получения и отображения файлов (изображений и PDF-файлов) с сервера Flask.
Приведенный ниже код Flask загружает файлы в хранилище BLOB-объектов Azure и генерирует URL-адреса SAS (подпись общего доступа) для загрузки этих файлов.
Добавлен Cors CORS(app, resources = {r"/upload/*": {"origins": "*"}})
, чтобы разрешить использование Cors со всех URL-адресов. Без этого я столкнулся с проблемой Cors в Flutter
.
from flask import Flask, render_template, jsonify, request
.....
app = Flask(__name__)
CORS(app, resources = {r"/upload/*": {"origins": "*"}}) # Specify the resource path for CORS
.....
....
Обязательно добавьте Cors в учетную запись хранения.
Код Flutter извлекает и отображает изображение из конечной точки URL-адреса с помощью HTTP-запросов.
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(),
),
);
}
}
Отображение изображения:
При отображении PDF я получаю черную страницу. Я следовал этому документу и использовал flutter_pdfview, но столкнулся с ошибкой «TargetPlatform.windows еще не поддерживается плагином pdfview_flutter».
В документации этого пакета четко сказано, что flutter_pdfview поддерживается только для платформ iOS и Android. Вместо этого рассмотрите пакет syncfusion_flutter_pdfviewer.
Спасибо! Рассмотрю syncfusion, что кажется удивительным, но в первую очередь для корпораций.
Частично решено! Я понял, что неправильно обозначил переменную в своем коде upload.dart (перед страницей uploaded.dart), а также добавил CORS в свою учетную запись хранения. Сейчас работаю с использованием iframe, но мне удалось открыть PDF-файл на новой странице.